Building chat app with Flutter and Firebase

Hi all, in this article, we’ll try to build a chat app with Flutter and Firebase from scratch (include Firebase Auth, Google Sign-In, Cloud FireStore and Firebase Storage). Our app will have the following features:

  • Sign-in by Google account.
  • Chat one to one with other users (send text, image and sticker).
  • Update profile and avatar.

Demo


1. Creating project on Firebase

Following this video to connect your flutter app to Firebase.

Note:

First, if you would like to use Google sign-in, need to do this step (only for Android - the video above doesn’t do it since he doesn’t use sign-in method) to get SHA-1 key (get the debug certificate is enough).

Second, add this line #import <Firebase/Firebase.h> to AppDelegate.h

Start the Cloud Firestore and Storage

Then enable sign-in method with Google

Now you’re done this step, don’t need to add or init any data manually at Firebase.

Rules at Storage:

Use this mode for storage to accept only image file type

The rule check image file type only works for Android (iOS always not match this condition even you upload the image). Because the image upload at Android is image/jpeg or image.png… but on iOS is just application/octet-stream no matter what the file type is.

So you can temporarily remove the second condition or change it to

if true && (request.resource.contentType.matches('image/.*') || request.resource.contentType.matches('application/octet-stream'));

to pass iOS file type.

If anyone finds a better solution, please tell me.

Rules at Database:

And this mode for database

2. Installing plugins

Note: at this time, you should use the version packages like me in pubspec.yaml, if you use the latest (like Google’s packages, they use AndroidX and will not compatible with the other packages like cached_network_image, flutter_toast… so the project can’t build success). More details at here: https://flutter.dev/docs/development/packages-and-plugins/androidx-compatibility#how-to-migrate-a-flutter-app-to-androidx

Following these instructions to add firebase_auth, google_sign_in, cloud_firestore, firebase_storage to your project.

These plugins help login app by Google account, get user info, real-time database for chatting and upload image.

And the others will help us show toast, cache data local, pick images from gallery, cache network image and parse date time.

3. Layout the login screen

When user login success, the return variable firebaseUser contains some user information, like displayName, photoUrl

then we’ll check if the user is new or not (by query to the server does this ID exist). If they’re a new user, write it to the database.

The other things we need to do here are writing user info to local storage (to use in other screens, mention later) and navigate to the main screen when login successful.

For more details, please check out the source code at the end of this article, in the path lib/login.dart

4. Layout the main screen

This screen contains list all users in our database, we’ll show all the info we got like avatar, nickname and about me. If you would like to design user has more field than that, create a detail screen to see all particular user info is a good idea.

Note: You should use two accounts for login (A and B). So when you log in account A, you can see B and vice versa.

Get list user quite simply by using StreamBuilder like below:

And about catching event when user touch back button at this screen, will exit the app, but we should show dialog confirm at first, like this

To catch back event, use WillPopScope widget, it gives an attribute name onWillPop, when user touch on soft or hard back button, it goes to this function first, and at this function, we open the dialog.

One more thing we have to do at this screen is to create a option menu to go to Settings screen or Log out. Our option menu is like this

About how to create option menu and dialog, please check my source code in the path lib/main.dart

5. Layout the settings screen

At here, the user can change some info like their avatar, nickname and about me. You can add more field than that if you want.

At first, we load current user info from disk (the data we wrote when they log in) to show on UI.

If user change avatar, we need to upload that file to Firebase Storage, then get file’s URL.

After that, update user info to Cloud FireStore with new photoUrl we just set.

Notice that a user just need an avatar, so we set file name is userID, if user uploads a new file, Firebase will replace the old with the new one.

More details, please check my source code in the path lib/settings.dart

6. Layout the chat screen

This is a score screen so we have many things to do on this screen.

First, we need to layout the UI, root widgets are like this

The root is WillPopScope because we need to handle the back press, if sticker or keyboard is shown, we need to hide it instead of back to the previous screen.

Second is handle sticker and keyboard appearance, if keyboard being shown, the sticker will dismiss and vice versa.

Keyboard show, sticker hide and vice versa

We need a variable isShowSticker to detect should show sticker or not, focusNode help us control should focus on text input or not, if focusing, the keyboard will appear.

Third is load previous message and listening for new income message, we’ll use StreamBuilder

Notice that when user A (current user) chat with user B (peer user), the problem is which groupChatId we’ll use to read and write data chat to the server since conversation of AB need save at the same node? So my idea is hashing the A’ uid and B’ uid, then creates a string like this.

Then at user A or B, they both read and write data at the same Firebase node.

At the time of this article is published, I found no way to scroll item to bottom list programmatically when receive a new message, so my idea is reverse array message (set attribute .orderBy when query data from the server), then set reverse attribute in ListView to true. Then list message will have a good UX like other apps chat.

We’ll only display the last 20 messages of the chat to avoid displaying a very long history on load.

Fourth is handle sending messages, if user sends an image, we need to upload this image and get URL, then we just add the content of the message is this URL, and stickers (are saved at the local project) just need the name of the file is enough.

When receiving a new message, we’ll check type first, if the type is image, we load this image from network, if the type is sticker, show it from the local file.

More details, please check my source code in the path lib/chat.dart


Cloud Firestore structure

Our database should be like this, just for checking if you need to.

Node users
Node messages

Congratulations

Now we are done 👏

If you guys have any questions. Please feedback so we can discuss and improve each other.

My source code is available at:

Want to make a web? Visit here:

Thanks for taking the time to read.