Flutter apps With AWS Amplify Backend: Part 2 — Authentication
Setting up authentication in a Flutter application is easy with the help of AWS Cognito, now available through Amplify Flutter. If you haven’t check out Part 1 of this series, I suggest you do that first.
What are we making?
We will be making a simple registration and login system for a Flutter application that uses AWS Cognito as the authentication backend.
As always, you can find the code for this tutorial at our Github repo.
What do we need?
The only thing you need for this tutorial is the app you created in Part 1 of this tutorial series, which is just a sample app with *amplify *initialized.
If you don’t want to read part 1 and you are already familiar with Amplify Flutter, then simply create a new Flutter application and connect it to a cloud instance of Amplify.
Let’s Get Started
Create a Basic Amplify Project
If you are using the app you created in Part 1, you can skip this step.
Otherwise, create a new flutter project, and from the project directory run:
amplify init
Add AWS Cognito to our Project
From the integrated terminal, run
amplify add auth
This adds AWS Cognito* *to your project. After following the setup wizard, your terminal should look something like this:
Note that we are going to be using emails as the authentication method. I will cover the other methods in future tutorials.
Next, follow the instructions given in the terminal and run amplify push to deploy authentication to your cloud project.y
Checking on the Status of our Resources
You can check the status of your cloud resources by running amplify status.
At this point, a user pool should have been created in the cloud for your AWS Cognito service. Go to your user pools (make sure you select your region) and you should see a new user pool.
Interacting with Cloud Resources
A few steps are needed to interact with the cloud, if you just want the final result and don’t care about the step-by-step, go here for a checkpoint commit.
Many of these code snippets are derived from the AWS documentation. I will give explanations about what each logical component does, and provide you with a working example of a login form.
- Add packages to pubspec.yaml
This gives us access to the dart packages for amplify.
amplify_core: '<1.0.0'
amplify_auth_cognito: '<1.0.0'
- Amplify Initialization
This code snippet initializes our authentication plugin (and any other plugins we may have) and sets our apps flags so that we can handle an unsuccessful cloud connection in our app.
void _configureAmplify() async {
if (!mounted) return;
// add all of the plugins we are currently using
// in our case... just one - Auth
AmplifyAuthCognito authPlugin = AmplifyAuthCognito();
amplifyInstance.addPlugin(authPlugins: [authPlugin]);
await amplifyInstance.configure(amplifyconfig);
try {
setState(() {
_amplifyConfigured = true;
});
} catch (e) {
print(e);
}
}
3. Register a User
Registering a user makes use of text controllers (not shown here). Once again, state flags (isSignUpComplete in this case) are set based on the result of the SignUp operation.
Future<String> _registerUser(LoginData data) async
{
try {
Map<String, dynamic> userAttributes = {
"email": emailController.text,
};
SignUpResult res = await Amplify.Auth.signUp(
username: data.name,
password: data.password,
options: CognitoSignUpOptions(
userAttributes: userAttributes
)
);
setState(() {
isSignUpComplete = res.isSignUpComplete;
print("Sign up: " + (isSignUpComplete ? "Complete" : "Not Complete"));
});
} on AuthError catch (e) {
print(e);
return "Register Error: " + e.toString();
}
}
4. Sign In a User
Future<String> _signIn(LoginData data) async {
try {
SignInResult res = await Amplify.Auth.signIn(
username: data.name,
password: data.password,
);
setState(() {
isSignedIn = res.isSignedIn;
});
if (isSignedIn)
Alert(context: context, type: AlertType.success, title: "Login Success").show();
} on AuthError catch (e) {
print(e);
Alert(context: context, type: AlertType.error, title: "Login Failed").show();
return 'Log In Error: ' + e.toString();
}
}
And that’s it! Those are all of the logical building blocks that we will use to create a nice signup and login interface. The only thing left to do is integrate these components into a user interface, which we will do next.
Flutter Login
For convenience, we will use a flutter package called flutter_login to make our first integration quick and painless. As with all things in life, KISS.
Main.dart
import 'package:amplify_auth_cognito/amplify_auth_cognito.dart';
import 'package:amplify_core/amplify_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_login/flutter_login.dart';
import 'amplifyconfiguration.dart';
void main() {
runApp(MaterialApp(
home: MyApp(),
));
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
// gives our app awareness about whether we are succesfully connected to the cloud
bool _amplifyConfigured = false;
// Instantiate Amplify
Amplify amplifyInstance = Amplify();
// controllers for text input
final emailController = TextEditingController();
final passwordController = TextEditingController();
bool isSignUpComplete = false;
bool isSignedIn = false;
@override
void initState() {
super.initState();
// amplify is configured on startup
_configureAmplify();
}
@override
void dispose() {
// Clean up the controller when the widget is removed from the
// widget tree.
emailController.dispose();
passwordController.dispose();
super.dispose();
}
void _configureAmplify() async {
if (!mounted) return;
// add all of the plugins we are currently using
// in our case... just one - Auth
AmplifyAuthCognito authPlugin = AmplifyAuthCognito();
amplifyInstance.addPlugin(authPlugins: [authPlugin]);
await amplifyInstance.configure(amplifyconfig);
try {
setState(() {
_amplifyConfigured = true;
});
} catch (e) {
print(e);
}
}
Future<String> _registerUser(LoginData data) async
{
try {
Map<String, dynamic> userAttributes = {
"email": emailController.text,
};
SignUpResult res = await Amplify.Auth.signUp(
username: data.name,
password: data.password,
options: CognitoSignUpOptions(
userAttributes: userAttributes
)
);
setState(() {
isSignUpComplete = res.isSignUpComplete;
print("Sign up: " + (isSignUpComplete ? "Complete" : "Not Complete"));
});
} on AuthError catch (e) {
print(e);
return "Register Error: " + e.toString();
}
}
Future<String> _signIn(LoginData data) async {
try {
SignInResult res = await Amplify.Auth.signIn(
username: data.name,
password: data.password,
);
setState(() {
isSignedIn = res.isSignedIn;
});
} on AuthError catch (e) {
print(e);
return 'Log In Error: ' + e.toString();
}
}
@override
Widget build(BuildContext context) {
return SafeArea(
child: FlutterLogin(
logo: 'assets/vennify_media.png',
onLogin: _signIn,
onSignup: _registerUser,
onRecoverPassword: (_) => null,
title:'Flutter Amplify'
),
);
}
}
Of course, add the following lines to your **pubspec.yaml **to enable our new packages:
flutter_login: '<1.0.0'
rflutter_alert: '^1.0.3'
You will need to add your own image to assets for the flutter_login logo
Testing User Registration
To register a new user, simply use the intuitive flutter_login interface to submit a new user request to Amplify Cognito.
Use a fake email, or a real one..your choice.
Navigate to your user pool and you should see a new user that was created:
In this case, I created two users
Select one of the users in your pool. Notice that the status is ‘UNCONFIRMED’. In this tutorial, we will be confirming our users manually simply by tapping on the ‘Confirm User’ button. In a future tutorial, I will demonstrate how to confirm users automatically using a verification code sent to their phone or email.
Tap ‘Confirm User’ to activate the new account
Testing User Login
Let’s try to login using the user that we just created.
Wrapping Up
Still with me? At this point, we have a very basic authentication system for our app that leverages AWS Cognito. There are still several other topics to explore in regards to authentication, but they are beyond the scope of this tutorial. Some other challenges we need to tackle include:
Authentication based on different credential types
Automatic confirmation of users
2FA and custom authentication processes
I hope to cover these topics in detail in a future article. Until then, let’s continue on our journey of nailing down the basics of AWS Amplify with Flutter. Next up — analytics.
This Series
Part 1: Basic Setup
Part 2: Authentication
Part 3: Analytics
Part 4: Storage
Top comments (3)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.