aws_s3 0.1.1+hotfix.5

  • Readme
  • Changelog
  • Example
  • Installing
  • 86

aws_s3 #

This plugin developed to make it easy to upload any file(s) to AWS S3 without writing Android, and IOS code separately using method channel.

DISCLAIMER: This is not an AWS officially released plugin but this plugin uses AWS official Android native and IOS native AWS plugins (So nothing to be worried). Check the package implementation on github: https://github.com/blasanka/aws_s3

Contributors are highly welcome.

To use this package, you have to create a instance of AwsS3 with parameters like below code snippet:

AwsS3 awsS3 = AwsS3(
  awsFolderPath: "your aws folder path",
  file: "file is of type File",
  fileNameWithExt: "file name",
  poolId: "your aws pool id",
  region: "your region using enum Regions",
  bucketName: "your bucket name to upload");

AwsS3 class, parameters:

  final File file;
  final String fileNameWithExt;
  final String awsFolderPath;
  final String poolId;
  final Regions region;
  final String bucketName;

  AwsS3({
    @required this.file,
    @required this.fileNameWithExt,
    @required this.awsFolderPath,
    @required this.poolId,
    this.region = Regions.US_WEST_2,
    @required this.bucketName,
  });

All the available regions from official Amazon AWS S3 android are supported in this Flutter plugin:

/// Enumeration of region names
enum Regions {
  GovCloud,
  US_GOV_EAST_1,
  US_EAST_1,
  US_EAST_2,
  US_WEST_1,
  US_WEST_2, ///Default: The default region of AWS Android SDK
  EU_WEST_1,
  EU_WEST_2,
  EU_WEST_3,
  EU_CENTRAL_1,
  EU_NORTH_1,
  AP_EAST_1,
  AP_SOUTH_1,
  AP_SOUTHEAST_1,
  AP_SOUTHEAST_2,
  AP_NORTHEAST_1,
  AP_NORTHEAST_2,
  SA_EAST_1,
  CA_CENTRAL_1,
  CN_NORTH_1,
  CN_NORTHWEST_1,
  ME_SOUTH_1
}

Getting Started #

This project is a starting point for a Flutter plug-in package, a specialized package that includes platform-specific implementation code for Android and/or iOS.

For help getting started with Flutter, view our online documentation, which offers tutorials, samples, guidance on mobile development, and a full API reference.

0.1.1+hotfix.5 #

  • Accuracy of the progrecess percentage improved.
  • Example app file pick issue fixed

0.1.1+hotfix.2 #

  • IOS Regions not found issue fixed.
  • IOS upload status completion issue fixed.

0.1.1+hotfix.1 #

  • IOS upload failed bug fixed.
  • IOS upload percentage not updating bug fixed.

0.1.1 #

  • IOS support for uploading single file added.

0.1.0+hotfix.3 #

  • Upload percentage not receiving bug fixed.
  • Context exception fixed with documentation update.

0.1.0 #

  • Android support for AWS S3 single file upload.

example/lib/main.dart

import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:aws_s3/aws_s3.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';


void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "AWS S3 file upload demo",
      home: Scaffold(body: CreateMessage()),
    );
  }
}

class CreateMessage extends StatefulWidget {

  @override
  CreateMessageState createState() => CreateMessageState();
}

class CreateMessageState extends State<CreateMessage> {
  final formKey = new GlobalKey<FormState>();
  final TextEditingController _textController1 = new TextEditingController();
  final TextEditingController _textController2 = new TextEditingController();

  //To hold image paths after uploading to s3 for adding to db
  File selectedFile;

  //to ensure image is uploading from the native android
  bool isFileUploading = false;

  String poolId;
  String awsFolderPath;
  String bucketName;

  @override
  void initState() {
    super.initState();
    readEnv();
  }

  void readEnv() async {
    final str = await rootBundle.loadString(".env");
    if (str.isNotEmpty) {
      final decoded = jsonDecode(str);
      poolId = decoded["poolId"];
      awsFolderPath = decoded["awsFolderPath"];
      bucketName = decoded["bucketName"];
    }
  }

  // Pick image
  pickerModal(ctx) {
    showModalBottomSheet<void>(context: context, builder: _bottomSheetBuilder);
  }

  Widget _bottomSheetBuilder(BuildContext context) {
    return new Container(
      height: 260.0,
      child: new Padding(
        padding: const EdgeInsets.fromLTRB(0.0, 20.0, 0.0, 20.0),
        child: new Column(
          children: <Widget>[
            _renderBottomMenuItem(Icons.image, "Gallery photo",
                type: FileType.image),
            new Divider(
              height: 2.0,
            ),
            _renderBottomMenuItem(Icons.video_label, "Video",
                type: FileType.video),
          ],
        ),
      ),);
  }

  _renderBottomMenuItem(icon, title, {FileType type}) {
    var item = new Container(
      height: 60.0,
      child: Row(
        children: <Widget>[
          new Padding(
              padding: const EdgeInsets.all(10.0),
              child: new Icon(icon,
                  size: 40.0, color: Theme.of(context).primaryColorLight)),
          new Center(
              child: Padding(
                  padding: const EdgeInsets.symmetric(
                      vertical: 8.0, horizontal: 20.0),
                  child: new Text(
                    title,
                    style: new TextStyle(
                        fontSize: 22.0, fontStyle: FontStyle.normal),
                  ))),
        ],
      ),
    );
    return new InkWell(
      child: item,
      onTap: () async {
        Navigator.of(context).pop();
        if (type == FileType.video) {
          selectedFile = await FilePicker.getFile(type: FileType.video);
        } else if (type == FileType.image) {
          selectedFile = await FilePicker.getFile(type: FileType.image);
        }
      },
    );
  }

  Widget buildGridView(BuildContext context) {
    return Wrap(
      children: List.generate(2, (index) {
        var content;
        if (index == 0) {
          // Add picture button
          var addCell = InkWell(
            onTap: () => pickerModal(context),
              child: Container(
                width: 70,
                height: 72,
                child: Icon(
                  Icons.add,
                  size: 50.0,
                  color: Theme.of(context).primaryColorLight,
                ),
              ),
          );
          content = addCell;
        } else {
          // Selected image
          content = Container(
            decoration: BoxDecoration(
              color: const Color(0xFFECECEC),
              border: Border.all(color: Colors.transparent),
            ),
            child: (selectedFile != null)
                ? Image.file(
              selectedFile,
              fit: BoxFit.cover,
              width: 70.0,
              height: 70.0,
            )
                : SizedBox(),
          );
        }
        return new Container(
          margin: const EdgeInsets.all(2.0),
          color: const Color(0xFFECECEC),
          child: content,
        );
      }),
    );
  }

  Widget deleteActionIconWidget(Function action) {
    return FloatingActionButton(
      child: Icon(
        Icons.delete,
        color: Colors.redAccent,
      ),
      onPressed: action,
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        elevation: 0.0,
        backgroundColor: Colors.white,
        centerTitle: true,
        title: new Text(
          "Compose",
          style: TextStyle(
            color: Theme.of(context).primaryColor,
            fontSize: 21.0,
            fontWeight: FontWeight.w400,
          ),
        ),
        iconTheme: IconThemeData(color: Theme.of(context).primaryColor),
      ),
      body: GestureDetector(
        onTap: () => FocusScope.of(context).requestFocus(new FocusNode()),
        child: SingleChildScrollView(
          child: Form(
            key: formKey,
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                Padding(
                  padding: const EdgeInsets.only(
                      left: 13.0, right: 13.0, top: 15.0),
                  child: Row(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: <Widget>[
                      Container(child: Text("To:")),
                      Container(width: 5.0),
                    ],
                  ),
                ),
                Container(
                  padding: const EdgeInsets.only(
                      left: 13.0, right: 13.0, top: 15.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: <Widget>[
                      Text(
                        "Subject " + "*",
                        style: TextStyle(
                            color: Colors.black87, fontSize: 14.0),
                      ),
                      TextFormField(
                          controller: _textController1,
                          validator: (String text) {
                            if (text.length == 0)
                              return "Title cannot be empty";
                            else
                              return null;
                          },
                          decoration: InputDecoration(
                              hintText: "Subject",
                              border: OutlineInputBorder(),
                              hintStyle: TextStyle(
                                  color: Colors.black, fontSize: 11.0))),
                    ],
                  ),
                ),
                Container(
                  padding: const EdgeInsets.only(
                      left: 13.0, right: 13.0, top: 15.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: <Widget>[
                      Text(
                        "Message *",
                        style: TextStyle(
                            color: Colors.black87, fontSize: 14.0),
                      ),
                      TextFormField(
                          controller: _textController2,
                          maxLines: 10,
                          validator: (String text) {
                            if (text.length == 0)
                              return "Type a message";
                            else
                              return null;
                          },
                          decoration: InputDecoration(
                              hintText: "Type your message here...",
                              border: OutlineInputBorder(),
                              hintStyle: TextStyle(
                                  color: Colors.black87, fontSize: 11.0))),
                    ],
                  ),
                ),
                new Container(
                  padding: const EdgeInsets.symmetric(
                      horizontal: 10.0, vertical: 5.0),
                  child: new Builder(
                    builder: (ctx) {
                      return buildGridView(context);
                    },
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(
          Icons.send,
          color: Colors.white,
        ),
        onPressed: submitMessage,
      ),
    );
  }

  Future<String> _uploadImage(File file, int number,
      {String extension = 'jpg'}) async {

    String result;

    if (result == null) {
      // generating file name
      String fileName =
          "$number$extension\_${DateTime.now().millisecondsSinceEpoch}.$extension";

      AwsS3 awsS3 = AwsS3(
          awsFolderPath: awsFolderPath,
          file: selectedFile,
          fileNameWithExt: fileName,
          poolId: poolId,
          region: Regions.AP_SOUTHEAST_2,
          bucketName: bucketName);

      setState(() => isFileUploading = true);
      displayUploadDialog(awsS3);
      try {
        try {
          result = await awsS3.uploadFile;
          debugPrint("Result :'$result'.");
        } on PlatformException {
          debugPrint("Result :'$result'.");
        }
      } on PlatformException catch (e) {
        debugPrint("Failed :'${e.message}'.");
      }
    }
    Navigator.of(context).pop();
    return result;
  }

  Future displayUploadDialog(AwsS3 awsS3) {
    return showDialog(
      context: context,
      barrierDismissible: false,
      builder: (context) => StreamBuilder(
        stream: awsS3.getUploadStatus,
        builder: (BuildContext context, AsyncSnapshot snapshot) {
          return buildFileUploadDialog(snapshot, context);
        },
      ),
    );
  }

  AlertDialog buildFileUploadDialog(
      AsyncSnapshot snapshot, BuildContext context) {
    return AlertDialog(
      title: Container(
        padding: EdgeInsets.all(6),
        child: LinearProgressIndicator(
          value: (snapshot.data != null) ? snapshot.data / 100 : 0,
          valueColor:
          AlwaysStoppedAnimation<Color>(Theme.of(context).primaryColorDark),
        ),
      ),
      content: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 6),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Expanded(child: Text('Uploading...')),
            Text("${snapshot.data ?? 0}%"),
          ],
        ),
      ),
    );
  }

  Future<void> submitMessage() async {
    await _uploadImage(selectedFile, 1);

    debugPrint("Subject: " + _textController1.text);
    debugPrint("Message: " + _textController2.text);
  }
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  aws_s3: ^0.1.1+hotfix.5

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter pub get

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:aws_s3/aws_s3.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
73
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
100
Overall:
Weighted score of the above. [more]
86
Learn more about scoring.

We analyzed this package on May 26, 2020, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.8.1
  • pana: 0.13.8-dev
  • Flutter: 1.17.0

Health suggestions

Format lib/aws_s3.dart.

Run flutter format to format lib/aws_s3.dart.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.1.0 <3.0.0
flutter 0.0.0
Transitive dependencies
collection 1.14.12
meta 1.1.8
sky_engine 0.0.99
typed_data 1.1.6
vector_math 2.0.8
Dev dependencies
flutter_test