Flutter is an open-source mobile SDK from Google to build native applications for multiple platforms from a single codebase. Flutter uses a programming language called Dart, which is similar to combination of C++ and JS. It is a Object oriented, garbage collected, strongly typed language that can compile to JS or Native code directly.
Since I did not know any mobile app development technologies, this got my attention as learning this would mean I can build apps for multi-platforms without having to learn multiple languages or frameworks. This chatting application is one of my first app that I made while trying to learn Dart and Flutter. Now that I look back on the code, few thing could have be written better 😁.
Rather than looking at tutorial videos where the main focus will be on explaining flutter and the UI will be awful, I chose to search for ideas on design/artist networking websites such as dribble and found a design that is simple to make yet elegant. This is the final UI design idea I went ahead and implemented in this project.
Some of the applications features/capabilities that are added in this project -
- Signup/Login with emailID and password
- Auto update of contact/users list when new people register or update
- Chat auto scroll on new message arival
- Emoji keyboard
- Profile photo selection from camera or gallary
- Update display Name option
- Random background image (only visible to you in drawer screen)
- Notifications on new massages using FCM
- Store user list and meta information on device disk
Lets look at the brief overview of the application and backend. Since the intent of this project was to learn flutter, most of the heavy lifting is done in the client side and backend is mostly serverless implementation with the help of Firebase.
Here Firebase is the main endpoint for our chat app, for registering new users and authentication, chat messages and notifications.
Firebase Authentication -
Is used for new registration and auth of existing users. In this project we are only using auth by username-password combinations. Firebase also supports other auth types such as social media (Google, Facebook, Twitter), phone, Apple, GitHub etc..
Each user when they register will get a unique Auth ID in this section. This will be used thought out the application to identify the user and provide services to him. The Firebase console also neatly displays the registered users for the app along with UID, which will be helpful for debugging if needed.
Firebase Firestore -
Is the main runner of the show. It is a Realtime document database that we use for storing chats. When a user sends a new message, this database is updated and the other party listening to this particular document will also receive this update, without the need of polling.
In the project we use Firebase into two main documents - users and chats.
Users will maintain all registered users of the app, along with data such as Name, email, UID, Push notification ID, profile Image.
Chats will maintain the user chats with other users. Here the UID pair of the users between whom the conversation is happening is used as the key to store the messages. Hence chats between different users will have different UID pairs and will show as independent channels in the Firestore.
Along with the message content, other metadata such as sent time, From and To UID and message type is also saved. The type field is for extending this in the future to enable sending or GIFs or video links. In this case the type will be, for example 1 for GIF and 2-Video, and the content field will be treated as URL link to that media in such cases. This info can be used at the client side to provide rich display or render the media accordingly.
Firebase Storage -
Is a simple object storage service that is only used for storing the user profile picture. It is simple as it comes integrated within Firebase ecosystem and has access to user auth ID. This ID will be used to make sure that one user will store atmost one image file in this bucket. Once the user updates this profile image, the older file will be overwritten with new one. For now there is no option to send multimedia in this chat application, as this will add to the storage costs.
Firebase Cloud Messaging -
Is the successor of GCM and is a cross platform push notification service that is provided free of cost. Each user when they register for the chat app will also get a FCM ID. This will be stored in the user meta data on the Firestore User document.
In this project we are using this to send push notification to the user on arrival of new messages. After the client sends the message to the user, through Firestore, the client will also send a push notification request to the recipient FCM ID. If the recipient does not have the app open, it will show a notification of new message and short text content.
Unlike other services, push notifications sent from the client will not show up in the Firebase console, so no image for this section 😁.
Client code explanation
The main heart of the application is the Firebase SKDs or packages for each of its services in Dart.
firebase_core: ^1.0.1 firebase_auth: ^1.0.1 cloud_firestore: ^1.0.1 firebase_storage: ^8.0.0 firebase_messaging: ^9.0.0
These packages provide a simple and clean way to write your application logic by abstracting away all the API calls required to maintain connection and tokens to use the services. They also support Streams in Dart which is a great way to subscribe to the service and get updates in realtime when something changes.
For example in the below snippet you can see the Auth implementation, where if the
firebase_auth package will maintain the session info and will automatically update in case of session expiry so that the app can show login screen again.
This is the same approach used in the contacts screen as well as chat messages display screen. The client will subscribe to changes in the respective Firestore document and using
StreamBuilder in Dart, it will automatically refresh the widget to display the changes. So whenever a new message is received, the
ListView widget will be updated to show the new message without the need to write any additional code for this.
Apart from the basic chatting capabilities, this app also has a settings screen where the user can update his/her profile picture and display name. Here I am using the package
image_picker to provide feature of selecting image either from files or camera.
Once the user selects the new image, we can show the preview in the avatar section and after Update button is pressed, the client will upload this new image to Firestore Storage with user UID as the filename. This will return the image URL for the stored image, which will be updated in the user meta data of Firestore document.
The push notifications is something that I struggled a bit to implement. First important point I was not aware of is that this will not work properly in the Android Emulators, which I was using during the project to test out code. Second is that for the app to act on the push notifications, additional steps have to be done such as registering a callback function that will be called when the application in the background.
I have configured this so that when the user click on the notification message the app will open the chat screen of that corresponding user so that he can continue the chat. We get the From UID in the push notification, which can be passed to chat screen along with other parameters to open the right chat.
Sending a notification is straight forward. I am using
dio package to make the http POST to send the message to https://fcm.googleapis.com/fcm/send, since this is not provided as part of the
FCM notification structure has two main components - data message and notification message. Data can have custom key:value pairs and client is responsible for processing this section. Notification part is automatically displayed to the user as part of the client app. It has predefined keys like title and body.
The approach taken in this project is that all the chats and contacts are stored in Firebase. So each time user clicks on that app, the contact list is loaded from backend. Same for the chats, when user clicks on the contact, the corresponding chats are fetched and displayed. But I wanted to try out local storage/persistance as well. Here I am using a package called
hive. This a simple and fast key-value database. The reason this was chosen was that the data structure to be stored is not complex and the performance of hive is better that SQL.
In this project I am only storing contact list along with meta information like last message, timestamp, UID etc..
So in the totality your device will contain contact list of the chat app, data related to Firebase tokens and auth, cached images that were downloaded.
Clone and build your own app
Complete source code is available on GitHub along with instructions to get you started on building the app -