AWS Amplify is a great solution to easily build and host full-stack applications for both web and mobile. It has SDKs for iOS, Android, web, React Native, and Flutter.
AWS Amplify allows you to do a lot of helpful tasks when it comes to app development. For example, you can use AWS Amplify to:
- Create and connect to an app backend: Create a backend hosted on AWS and connect it using the cross-platform Amplify Flutter library, which gives you the ability to enable real-time data interactions with offline support.
- Host a web app: Easily deploy web app or website to the fast, secure and reliable AWS content delivery network with AWS Amplify Hosting.
- Use pre-built authentication UI: The AWS Authenticator gives you a ready-made UI component for your app.
In this tutorial we're going to be focusing on the backend functionality by adding AWS authentication to our Flutter project using email and password.
Let's get started! 🙌
The main goal of this tutorial is to create a new project with AWS Amplify that uses Very Good Ventures tools and best practices. We will create an app, enable the authentication service, and implement it in the app. This example will have a layered architecture (check out this article by my teammate Marcos Sevilla) with 100% test coverage, and our starter app will be generated by Very Good CLI ✨
You're going to see how easily the Very Good Ventures tools and best practices can be implemented with AWS Amplify.
Install the AWS Amplify CLI to be able to configure the project.
AWS Amplify configuration
The next step is to configure AWS Amplify and create an IAM User. This user enables you to manage users and user permissions in AWS. For more detail on creating an IAM User, refer to these docs.
1. Run the amplify configure command.
This command will ask you to sign into the AWS Console (you need to copy the URL that appears in the terminal). After signing in, the CLI will ask you to create an IAM user.
2. After filling out the IAM user information you need to continue in the web browser to manage the policy AdministratorAccess-Amplify in your account. Note that we're providing administrative access to the account in this example for the sake of simplicity, but you should follow best practices when it comes to admin access.
- Set the username (in this case I added vgv_amplify_user but you can use your own username).
- Select AdministratorAccess-Amplify for this user.
- Check the review page and create the user. As you can see, you have important information here, like the console URL, the access key ID, and the secret access key. Don't close this page, because we need to use these keys to complete the configuration
3. Go back to the terminal and set the keys that we saw in the last section. Set the Profile name as default.
AWS Amplify initialization
1. Run the command amplify init locally to create your Amplify project. Then, run amplify push. Here we're going to fill out a few questions to set up the project.
After finish the form, you should receive this success message. This means that your project is successfully initialized in the cloud.
You can check that everything is setup correctly if you sign into your AWS account and search for AWS Amplify. There you are going to see your project and the creation date.
2. The next step is to enable the Amplify Studio. With this, we're going to have an Admin UI that allows us to control the project in the cloud very easily.
3. We now have a new folder in the current directory which contains all the Amplify configurations.
Also, we will have a new file in the lib folder called amplify_configuration.json (note that we renamed the file from the generated name: amplifyconfiguration.json). This file will contain the configurations of the services that we want to use (in this example, authentication). But for now, it shouldn't contain many lines, since we haven't configured authentication yet.
Add authentication to AWS Amplify
We are going to use the AWS Amplify CLI to add the authentication configuration. We need to run amplify add auth command to start. Then we need to answer a few questions:
After completing these questions, it is important to run the amplify push command because this is going to push all the changes that we made to the cloud.
After a few minutes, you should see this message in the terminal: All resources are updated in the cloud. You can check that the authentication service is deployed in Amplify Studio.
If you check your amplify_configuration.dart file (note that we renamed the file from the generated name: amplifyconfiguration.dart). You can now see that there are new lines within regarding authentication.
Now, we have the authentication service deployed in the cloud, but we need to implement it in our Flutter application.
Authentication settings in Amplify Studio
Now we're going to configure the authentication settings in Amplify Studio. Here we'll be able to see password protection settings and change the body of the email that is sent to the user when the user signs up.
- Go to Amplify Studio and select the project that you created.
- Go to password protection settings and change the password policy.
- Go to verification message settings and change the body of the email that is sent to the user when the user is signing up.
- Deploy the changes.
After deploying the changes, we need to pull them. To do that, go to the terminal and run the following command at the root of your Flutter application.
Then you should see the following message in the terminal.
Create a new Flutter app (using the default Very Good Core template).
With your AWS Amplify project now setup, we can now create the Flutter application using Very Good CLI.
1. Activate Very Good CLI.
2. Create a new Flutter project.
Create the auth client package
Now is time to create a new package called auth_client in the packages folder (you should create the packages folder in the root). This package is going to contain all the code related to the authentication service.
We're going to use the amplify_auth_cognito package to implement the authentication service in our Flutter application. Also, we're going to use the amplify_flutter package to connect our Flutter application with the AWS Amplify project.
We need to add these packages to our pubspec.yaml file into the auth_client package. Note that we're using the latest stable versions of these packages, but you can check out the developer preview versions, which include cross-platform support for web and desktop.
1. Add the amplify_auth_cognito package to the pubspec.yaml file.
2. Add the amplify_flutter package to the pubspec.yaml file.
Once we have all the dependencies added, we can start with the implementation. This is the structure of the auth_client package.
In the auth_client file, we're going to create a class called AuthClient that will be the entrypoint to the authentication service. This class will have all the methods that we need to implement the authentication service:
- signUp will sign up a new user
- confirmSignUp will confirm the sign up of a new user
- signIn will sign in a user
- signOut will sign out a user
- onHubEvent will listen to the events that are happening in the authentication service.
As you can see there are custom exceptions that you can find here.
Create the user repository package
The user repository will interact with the auth_client package. We're creating this package to abstract the implementations details of the Amplify auth_client from the rest of our application, according to our layered architecture. The structure is the following:
1. Create a new package called user_repository and add the auth_client package as a dependency to the pubspec.yaml file:
Once we have the package created, we can start working on the user_repository.dart file.
2. Create a new file called user_repository.dart and add the following code:
Now we have the user_repository package ready to be used in the blocs.
Update pubspec.yaml in the root folder
We need to add the user_repository package and auth_client as a dependencies to the pubspec.yaml file in the root folder:
Create the needed features
We're going to create the following features in the lib folder:
- Sign in
- Sign up
- Sign out
- Confirmation code
Create the needed blocs
We're going to create different blocs for each feature. You can create a feature quickly using flutter_bloc_feature brick. This brick is very useful to create all the structures for our feature. To use it you need to activate mason, if you don't know what mason is, you can find more information about it here.
Let's get started! 🙌
- app_bloc.dart: This bloc will be responsible for the authentication flow. It will listen to the authStatus stream and emit the correct state. Additionally, it will handle the sign-out method.
You can check the app_event and the app_state files here.
- sign_in_bloc.dart: This bloc will be responsible for the sign-in flow. It will listen to the SignInEvent event and emit the correct state. Additionally, it will have events to update the state of the form.
As you can see in here, we are using Formz to handle the form state. Formz is a package that provides a set of classes that can be used to handle the state of a form.
There is a package called form_inputs into packages folder. This package is a wrapper of Formz that provides some custom inputs that can be used in the forms. You can find it here.
You can check the sign_in_event and the sign_in_state files here.
- sign_up_bloc.dart: This bloc will be responsible for the sign-up flow. It will listen to the SignUpEvent event and emit the correct state. Additionally, it will have events to update the state of the form.
You can check the sign_up_event and the sign_up_state files here.
- confirm_sign_up_bloc.dart: This bloc will be responsible for the sign-up flow confirmation. It will listen to the ConfirmationCodeEvent event and emit the correct state. Additionally, it will have events to update the state of the form.
You can check the confirmation_code_event and the confirmation_code_state files here.
Create the UI in each feature
Now that we have the blocs ready, we can start creating the UI for each feature.
- sign_in_page.dart: This is the page for the sign-in feature. It will be responsible for creating the bloc and the SignInView widget, as well as initializing the SignInBloc.
- sign_in_view.dart: This is the view of for the sign-in feature. It will be responsible for creating the UI and interacting with the bloc to sign in and check the status of the form.
- sign_up_page.dart: This is the page that will be displayed when the user clicks on the Sign Up button. It will be responsible for displaying the SignUpView widget and providing the SignUpBloc to it.
- sign_up_view.dart: This view will be responsible for displaying the sign-up form and handling the user interactions with it. Also, it is going to be in charge of the SignUpBloc which handles the sign-up process.
You can see that we're using a showMaterialModalBottomSheet to show the confirmation code page. This is possible thanks to the modal_bottom_sheet package, created by our teammate Jaime Blasco, you can find more types of modal bottom sheets in the package documentation.
- confirmation_code_page.dart: This page will be responsible for displaying the confirmation code form and handling the user interactions with it.
- confirmation_code_form.dart: This widget will be responsible for displaying the confirmation code form modal and handling the user interactions with it. It will also be in charge of the ConfirmationCodeBloc which handles the confirmation code process.
- app.dart: This page will initialize the AppBloc and the UserRepository. We will use a boolean to check if the user is authenticated or not. This widget will also be responsible for displaying the app_view widget.
- app_view.dart: This widget will be responsible for displaying the correct view depending on the auth_state. To control which view should be displayed, we're going to use FlowBuilder and the status store in the AppBloc.
We can have three possible states to display the needed view:
- AppStatus.authenticated: This state will be displayed when the user is authenticated. In this case we're going to display the HomePage widget.
- AppStatus.unauthenticated: This state will be displayed when the user is not authenticated. In this case we're going to display the SignInPage widget.
- AppStatus.sessionExpired: This state will be displayed when the user session has expired. In this case we're going to display the SignInPage widget.
- home_page.dart: This page will be displayed when the user is authenticated. In this page we're going to display the home_view widget.
- home_view.dart: This view is the last view that we're going to display. If the user can see this view it means that the user is authenticated. Here, we're have the possibility to sign out the user.
Add configure Amplify method to the bootstrap file
Now we're going to add the configureAmplify method to the bootstrap.dart file. This method will be responsible for configuring the Amplify plugins and the AmplifyAuthCognito plugin.
Call it into bootstrap method.
We've seen how easy is to use AWS Amplify and implement a sign-in and sign-up flow in a Flutter application using the AmplifyAuthCognito plugin and the Very Good Ventures tools and best practices. One thing we appreciate about using AWS Amplify is that it's very well-documented!
In addition to authentication, AWS Amplify provides you with other utilities such as:
- GraphQL API
- REST API
- UI Library
If you are thinking to build an application that requires authentication, data modeling, storage and more. AWS Amplify is a great option.
Hope you enjoyed it! If you are interested in testing this application, check out our GitHub repository with this example.