Building chat app with Flutter and Firebase
Major update:
Aug, 27th, 2022 — Upgrade dependencies and fix to work fine with Flutter 3
Jun, 4th, 2021 — Migrating to Flutter 2 and using Dart sound null safety
Oct, 2th, 2021 — Apply provider & restructure
In this article, we’ll try to build a simple chat app with Flutter and Firebase from scratch (including Firebase Auth, Google Sign-In, Cloud FireStore, and Firebase Storage). The app will have the following features:
- Sign-in by Google account.
- Chat 1–1 with other users (send text, image, sticker).
- Update profile and avatar.
- Push notification (refer to the extension article here)
Maybe some template code in this article is outdated, please check the source code for the latest.
TLDR: check source code at the end of this article.
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 won’t do it since he doesn’t use sign-in method) to get SHA-1 key (get the debug certificate is enough).
- https://developers.google.com/android/guides/client-auth
- Remember that the keytool only accepts one parameter, but two “-exportcert -list” are suggested. The “-exportcert” parameter is redundant and can be omitted from the commands listed on that page.
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 with this step, don’t need to add or init any data manually at Firebase.
Rules at Storage:
The rule check image file type only works for Android (iOS does always not match this condition even if you upload the image). Because the image upload on 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:
2. Installing plugins
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 page
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 pages, mention later) and navigate to the main page when login successful.
For more details, please check out the source code at the end of this article, in the path lib/auth_provider.dart
4. Layout the home page
This page 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 page to see all particular user info is a good idea.
Get list user quite simply by using StreamBuilder
like below:
One more thing we have to do at this page is to create a option menu to go to Settings or Log out page. Our option menu will like this
About how to create option menu and dialog, please check my source code in the path lib/pages/home_page.dart
5. Layout the settings page
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 one user just need one 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/pages/settings_page.dart
6. Layout the chat page
This is the core page so we have many things to do on it.
First, need to layout the UI, root widgets are like this
Need PopScope
because we have to set chattingWith
value to null when exit page (condition to trigger push notification)
Second is handle sticker and keyboard appearance, if keyboard being shown, the sticker will dismiss 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 A & B need to 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 Cloud Firestore 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.
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/pages/chat_page.dart
Cloud Firestore structure
Our database should be like this, just for refer if you need to.
Congratulations
Now we’re done 👏
Source code is available at
Extension — push notification messages
Want to make a web? Visit here