Flutter is Google’s UI toolkit for building beautiful, natively compiled applications for mobile, web, and desktop from a single codebase, and is one of the fastest growing mobile frameworks in 2020. AWS Amplify recently announced Flutter support in Developer Preview, meaning it's not officially launched yet, but we can start trying it out now.
Here's my log from getting a simple Photo-Sharing app going with Authentication and Storage (example is in Android, but should work for iOS and web too).
Explained In 100 Seconds
- YouTube: https://youtu.be/0NgR-Qa5GPE
- Twitter: https://twitter.com/swyx/status/1298349861696282625?s=20
- Dev.to Embed:
Start a new Flutter App
Assuming you've set up all the prerequisites for Flutter (including setting up an Android Virtual Device and accepting the Android SDK licenses, you can head to File -> New -> New Flutter Project and pick the basic Flutter Application starter.
We'll need to add the new Amplify flutter libraries, so be sure to add them in pubspec.yaml
and Pub get
to download:
dependencies:
flutter:
sdk: flutter
# new
amplify_core: '<1.0.0'
amplify_auth_cognito: '<1.0.0'
amplify_storage_s3: '<1.0.0'
# we happen to also use this in our demo
file_picker: ^1.13.3
## this is also available if you want analytics but not covered in this post
# amplify_analytics_pinpoint: '<1.0.0'
Initialize an Amplify project
Note: If you're reading this in late August 2020, be aware that you'll need a special version of the CLI because it is still in developer preview:
npm i -g @aws-amplify/cli@4.26.1-flutter-preview.0
We then follow the standard Amplify setup instructions (assuming CLI is installed and configured).
# command
amplify init
# prompts
? Enter a name for the environment
`dev`
? Choose your default editor:
`IntelliJ IDEA`
? Choose the type of app that you're building:
'flutter'
⚠️ Flutter project support in the Amplify CLI is in DEVELOPER PREVIEW.
Only the following categories are supported:
* Auth
* Analytics
* Storage
? Where do you want to store your configuration file?
./lib/
? Do you want to use an AWS profile?
`Yes`
? Please choose the profile you want to use
`default`
Notice that you will see a configuration file created in ./lib/
called amplifyconfiguration.dart
. This is a static file you can import that holds all your public facing Amplify data, but most people will only use it in amplify.configure(amplifyconfig)
.
We're going to want to set up storage for our photo app, which requires authentication. Fortunately the CLI sets up both at once for us:
# command
amplify add storage
# prompts
? Please select from one of the below mentioned services:
`Content (Images, audio, video, etc.)`
? You need to add auth (Amazon Cognito) to your project in order to add storage for user files. Do you want to add auth now?
`Yes`
? Do you want to use the default authentication and security configuration?
`Default configuration`
? How do you want users to be able to sign in?
`Username`
? Do you want to configure advanced settings?
`No, I am done.`
? Please provide a friendly name for your resource that will be used to label this category in the project:
`S3friendlyName`
? Please provide bucket name:
`storagebucketname`
? Who should have access:
`Auth and guest users`
? What kind of access do you want for Authenticated users?
`create/update, read, delete`
? What kind of access do you want for Guest users?
`create/update, read, delete`
? Do you want to add a Lambda Trigger for your S3 Bucket?
`No`
When you've set up whatever else you might need, you can push to the cloud to kick off the provisioning:
amplify push --y
This process takes a while to provision all the resources for you, so while that's going we can set up our code first.
Diving into Flutter code
Inside of ./lib/main.dart
, we can import everything we will need:
import 'package:amplify_auth_cognito/amplify_auth_cognito.dart';
import 'package:amplify_core/amplify_core.dart';
import 'package:amplify_storage_s3/amplify_storage_s3.dart';
import 'amplifyconfiguration.dart';
// etc
Then inside of our main page we can write the Amplify config code:
// inside of some class for the page
Amplify amplify = Amplify();
bool _isAmplifyConfigured = false;
@override
initState() {
super.initState();
_initAmplifyFlutter();
}
void _initAmplifyFlutter() async {
AmplifyAuthCognito auth = AmplifyAuthCognito();
AmplifyStorageS3 storage = AmplifyStorageS3();
amplify.addPlugin(
authPlugins: [auth],
storagePlugins: [storage],
);
await amplify.configure(amplifyconfig);
setState(() {
_isAmplifyConfigured = true;
});
}
This way we are able to add a little loading screen based on _isAmplifyConfigured
if we wish.
For the rest of the app, you will have to write up the Flutter UI experience yourself, but the hard part is over from writing Amplify code.
Using Amplify Auth in a Widget
Call the appropriate functions (docs here) as needed. Here's how we sign a user in:
import 'package:amplify_auth_cognito/amplify_auth_cognito.dart';
import 'package:amplify_core/amplify_core.dart';
import 'package:flutter/material.dart';
class SignInView extends StatefulWidget {
@override
_SignInViewState createState() => _SignInViewState();
}
class _SignInViewState extends State<SignInView> {
final usernameController = TextEditingController();
final passwordController = TextEditingController();
String _signUpError = "";
List<String> _signUpExceptions = [];
@override
void initState() {
super.initState();
}
void _signIn() async {
// Sign out before in case a user is already signed in
// If a user is already signed in - Amplify.Auth.signIn will throw an exception
try {
await Amplify.Auth.signOut();
} on AuthError catch (e) {
print(e);
}
try {
SignInResult res = await Amplify.Auth.signIn(
username: usernameController.text.trim(),
password: passwordController.text.trim());
Navigator.pop(context, true);
} on AuthError catch (e) {
setState(() {
_signUpError = e.cause;
_signUpExceptions.clear();
e.exceptionList.forEach((el) {
_signUpExceptions.add(el.exception);
});
});
}
}
// etc for the Widget build
Using Amplify Storage in a Widget
Call the appropriate functions (docs here) as needed. Here's how we upload a new picture:
import 'package:flutter/material.dart';
import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'package:amplify_core/amplify_core.dart';
import 'package:amplify_storage_s3/amplify_storage_s3.dart';
class ImageUploader extends StatelessWidget {
void _upload(BuildContext context) async {
try {
print('In upload');
// Uploading the file with options
File local = await FilePicker.getFile(type: FileType.image);
local.existsSync();
final key = new DateTime.now().toString();
Map<String, String> metadata = <String, String>{};
metadata['name'] = 'filename';
metadata['desc'] = 'A test file';
S3UploadFileOptions options = S3UploadFileOptions(
accessLevel: StorageAccessLevel.guest, metadata: metadata);
UploadFileResult result = await Amplify.Storage.uploadFile(
key: key, local: local, options: options);
print('File uploaded. Key: ' + result.key);
Navigator.pop(context, result.key);
} catch (e) {
print('UploadFile Err: ' + e.toString());
}
}
@override
Widget build(BuildContext context) {
return Column(children: [
RaisedButton(
child: Text("Upload Image"),
onPressed: () {
_upload(context);
},
)
]);
}
}
Full Demo App
If you would like to see a demo app with all this setup, head to the amplify-flutter
example repo: https://github.com/aws-amplify/amplify-flutter/tree/master/example
(and give it a ⭐ - it really helps!)
What's Next?
Being a new SDK, the Flutter Developer Preview is definitely lacking feature parity with the JS/Android/iOS SDK's. Here's what's coming in the next few months:
- API (REST/GraphQL): The API category provides an interface for retrieving and persisting your data, using GraphQL backed by AWS AppSync or REST APIs and handlers using Amazon API Gateway and AWS Lambda.
- DataStore: Amplify DataStore provides a programming model for leveraging shared and distributed data without writing additional code for offline and online scenarios, which makes working with distributed, cross-user data just as simple as working with local-only data.
- Predictions: The Predictions category enables AI/ML use cases on the frontend such as translating text, converting text to speech, and recognizing text, labels and faces in images.
- Auth: We will extend the Auth category with social sign in with Web UI
- Storage: We will extend the Storage category with the ability to track Upload and Download Progress
- Escape Hatches: We will add support for ‘escape hatches’, enabling developers to more easily use the low-level generated iOS and Android AWS Mobile SDKs for additional use cases.