60 Days of Flutter :Building a Messenger : Day 15–17 : Implementing Registration Screen using ‘flutter_bloc’
Hey There! In last post we took a look at how Bloc pattern works. Today we’ll implement it on the Registration Page of our app. We’ll be using flutter_bloc which makes implementing Bloc pattern easier.
If you have no idea what Bloc is, I strongly recommend you read the previous post in this series before moving ahead. Let’s get started.
Visualization
Before we start let’s visualize the events and the states we’re going to encounter during the whole process. Start by taking a look at both the pages on our registration screen.
Events
The Events can be seen as the user’s journey throughout this page. Let’s list them down in sequence.
States
Every time a Event is dispatched, the Bloc takes it and converts it into a State. In simple words, for every Event executed by the user, the UI responds by a predictable State. Check the state diagram below to understand the flow.
This should give you a rough idea of what the flow will be. And even if you’re confused you need not worry. It’ll get clearer as we move ahead.
Creating the Bloc Directory Structure
Add these packages under dependencies in your pubspec.yaml.
firebase_auth: ^0.14.0+4 #to auth with firebase
google_sign_in: ^4.0.7 #to auth with google account
flutter_bloc: ^0.21.0 #helps in implementing blocs
equatable: ^0.5.0 #helps in comparing class using their values
cloud_firestore: ^0.12.9+1 #Firebase Database
image_picker: ^0.6.1+4 #image picker used for picking images from gallery
firebase_storage: ^3.0.6 #storage for storing image filesWe’ll first start by creating the directory structure of our Bloc. I recommend using this plugin with Android Studio for quickly generating Bloc classes. Similar plugin is also available for VSCode users here.
Create directory blocs/authentication -> right click on it ->new->Bloc Generator -> New Bloc
This should generate the below bloc structure.
- AuthenticationBloc : Contains the
Bloclogic. - AuthenticationEvent : Contains the
Eventclasses. - AuthenticationState : Contains the
Stateclasses. - Bloc.dart : exports all the above classes as a package.
Setting up Firebase
Follow this guide to add Firebase to the project. once you’ve added the google-services.json and followed the steps mentioned in the above guide, go to your project on Firebase console and
- Enable Google Authentication
2. Go to Database tab, create a “Cloud Firestore” database and under the rules tab paste the following rules. These are basic CRUD rules to authenticate users before they access our data. Read this documentation for more info on this.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{document=**} {
function isSignedIn() {
return request.auth.uid != null;
}//Allow creating a new user to anyone who is authenticated
allow create: if isSignedIn();//Allow read if signed in
allow read: if isSignedIn();// Allow update only if the uid matches (same user)
allow update: if isSignedIn() && request.auth.uid == resource.data.uid;// Allow delete only if the uid matches (same user)
allow delete: if isSignedIn() && request.auth.uid == resource.data.uid;
}
}
}
3. Go to Storage Tab and enable it.
This should setup everything for our project.
Creating the Events
Let’s define all our events first
- AppLaunched : Dispatched by
main.darteverytime the app start. - ClickedGoogleLogin : Dispatched when the user clicks ‘Sign In with Google’ Button.
- LoggedIn : When the authentication is successful, this event is dispatched internally by the
Blocto save the details received from google auth (name, profile picture, email) to Firebase FireStore and then prefil on the second page. - PickedProfilePicture : This event is dispatched when the user picks an image. It basically passes the image file to the
Blocwhich triggers aStatechange and loads the image from new file in the thumbnail. - SaveProfile : This event is triggered when the user clicks the done FAB after filling out his age, username and other details.
- ClickedLogout : This event is not being used right now. We’ll later implement a logout button in the settings page of the app where this will be required.
Creating the States
The names of these states are pretty self explanatory. You’ll understand their usage, when we write the Bloc Logic.
Business Logic aka Bloc
The Bloc contains the event to state conversion logic.
The mapEventToState method takes the Events and yields the States as output. We are using three Repositories for our Bloc. yield is used with async streams in dart.yield adds a value to the output stream of the surrounding async* function. It's like return, but doesn't terminate the function. Refer this documentation for more details.
Creating the Repositories
- AuthenticationRepository
Responsible for creating, terminating and retrieving session details.
- UserRepository
Responsible for interacting with the FireStore Database, and saving the user data in it.
- StorageRepository
Responsible for interacting with the Firebase Storage, uploading image files (for profile pictures in this case) in it.
The Model Class
For now we have a single model class, User. We’ll add a factory to it which can directly convert a Firestore DocumentSnapshot to a User object.
We started by creating the Bloc and then the repositories. Recalling from the previous post, the next layer below these is the Provider layer. Let’s create them.
Let’s Provide !
On one of my previous posts in this series a interesting question was posed by suggesting me to architect the app in a way which makes switching from Firebase to say AWS/other services really easy. Let’s see what we can do.
Start by creating abstract base provider classes for all the providers.
then the concrete implementations for all the three providers.
How does it help?
Using abstract implementations of providers will help us swap out the providers and replace them with different ones in future. All the logic related to the interaction with network/db/auth can be kept here. The upper layers will have no knowledge of how the data is being processed in the lower layers. All they’ll care about is about interacting with the repository.
Hooking the BLoC
We have created all the bells and whistles we needed for our app’s business logic to work. Now time to hook it to our UI, RegisterPage.
The first thing we need to do is provide the Bloc in the main Messio App widget itself so that we can trigger the AppStarted Event as soon as the app starts. We’ll user the BlocProvider to wrap our existing Messio App Widget. The builder is where we’ll instantiate the AuthenticationBloc and give it it’s required repository instances.
Now we’ll go ahead and hook the events and states to the RegisterPage. We’ll do it in the same order as shown in the diagram at the start.
First, ‘Sign In with Google’ button should dispatch a ClickedGoogleLogin() Event.
In our initApp() method we would want to receive the resultant state i.e. Authenticated and switch to the next page in PageView.
To show the CircularProgressBar we’ll use a BlocBuilder. It’s similar to a StreamBuilder. Here we give it the bloc to be used and in the builder we receive the state updates.
Similarly we’ll also implement the Event dispatches and BlocBuilder to receive states in other parts of the RegisterPage . In the end the RegisterPage class should look something like this.
And the result should look something like this.
Clean and Nice !
In next post we’ll take a look at how to write test cases for the layers we created today.
Note: You’ll probably face some dex errors while building this code due to issues with firestore and image_picker packages. Compare your android/build.gradle , android/app/build.gradle and android/gradle.properties files to with the one in the project to fix it.
References
Visit this gist.
Code Changes
How Can You Contribute?
- Open issues with suggestion of better approaches or ideas for the app.
- Connect with me on Twitter or Linkedin or Instagram.
- Star the Github repository.
- Share the series on Twitter.
- Follow me on Github.
Posts In This Series
- 60 Days Of Flutter : Building a Messenger from Scratch
- 60 Days of Flutter : Day 1 : Creating the App
- 60 Days of Flutter : Day 2 : Setting Up A CI With Flutter
- 60 Days of Flutter : Day 3–4 : Building a Chat Screen in Flutter
- 60 Days of Flutter : Day 4–5 : Widget Testing With Flutter
- 60 Days of Flutter : Day 6–7 : Implementing a Slideable Widget Using Bottomsheet in Flutter
- 60 Days of Flutter : Day 8 : Changing The Launcher Icon and Implementing GestureDetector
- 60 Days of Flutter : Day 9–10–11 : Creating Awesome Register Screen in Flutter
- 60 Days of Flutter : Day 12–14 : Understanding BLoC Pattern in Flutter
- 60 Days of Flutter : Day 15–17 : Implementing Registration Screen using ‘flutter_bloc’
- 60 Days of Flutter : Day 18–19 : Unit Testing in Flutter using ‘ mockito’
- 60 Days of Flutter : Day 20–21 : Unit Testing a Bloc in Flutter
- 60 Days of Flutter : Day 22–23 : Building a Modern Contacts Page in Flutter
- 60 Days of Flutter : Day 24–26 : Building a Animated Progress Fab and the Contacts Bloc in Flutter
- 60 Days of Flutter : Day 27–29 : Sending and Retrieving Messages from Firebase using BLOC
- 60 Days of Flutter : Day 30–32 : Firebase Chat UI using Stream and Bloc
- 60 Days of Flutter : Day 33–35 : Paginating data from Firestore using Firebase Queries
- 60 Days of Flutter : Day 36–38 : Seamlessly Upload Files to Firebase Storage
- 60 Days of Flutter : Day 39–41 : One UI Inspired Attachments Showcase Page
- 60 Days of Flutter : Day 42–45 : Creating the Home Page & Quick Peek BottomSheet for Messages
- 60 Days of Flutter : Day 45–47 : Adding Dark Mode to a Flutter App
- 60 Days of Flutter : Day 48–50 : Creating the Settings Page using Bloc
- 60 Days of Flutter : Day 51–54 : Unit Testing Firebase Providers with Mockito
- 60 Days of Flutter : Day 55–56 : Deploying Firestore Security Rules using Firebase CLI
- 60 Days of Flutter : Day 60 : Wrapping It Up
Show Your Support
Press the clap button below if you liked reading this post. The more you clap the more it motivates me to write better!
