Building chat app with Flutter and Firebase

Quang Duy Tran
Aug 24, 2018 · 8 min read

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, sticker).
  • Update profile and avatar.
  • Push notification (refer to the extension article here)

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).

Image for post
Image for post

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

Start the Cloud Firestore and Storage

Image for post
Image for post
Image for post
Image for post

Then enable sign-in method with Google

Image for post
Image for post

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

Rules at Storage:

Image for post
Image for post

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:

Image for post
Image for post

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 screen

Image for post
Image for post

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

Image for post
Image for post

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

Image for post
Image for post

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

Image for post
Image for post

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

5. Layout the settings screen

Image for post
Image for post

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

Image for post
Image for post

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.

Image for post
Image for post

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.

Image for post
Image for post

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.

Image for post
Image for post
Image for post
Image for post

AndroidX compatibility issues

After cloning the project and building Android, if you see

Image for post
Image for post

Or something similar AndroidX. That’s mean you stuck in AndroidX compatibility issues. Don’t worry, we can solve it.

At the moment you have two choices to solve this issue

  • Migrating the project to AndroidX with Android Studio (recommended)

Benefits: can update to latest version plugins with the best support, improve performance.

Steps:

Updating version plugins, gradle, targetSdkVersion… has been completed in my source code. You don’t need to do it again. Your job is just to use Android Studio to migrate the project to Android X

After cloning the project, if you follow the docs, absolutely you’ll see this error (although we have done it)

Image for post
Image for post

Now right click on android directory in flutter project go to Flutter and click on Open Android module in Android Studio and then open it in New Window.

Image for post
Image for post

Now we really refactor here

Image for post
Image for post

If you want backup select the checkbox and click Migrate.

Image for post
Image for post

Click Do Refactor

Image for post
Image for post

Success

Image for post
Image for post

Now back to Flutter app windows and rebuild, everything will work like a charm.

  • Avoiding AndroidX (not recommended)

Switch to avoiding_androidx branch instead of master and just build the project.

Actually, I pin plugin dependencies in pubspec.yaml to the last major version from before they were migrated.

Image for post
Image for post

Then you will not get AndroidX compatibility issues.


Flutter Community

Articles and Stories from the Flutter Community

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store