60 Days of Flutter :Building a Messenger : Day 27–29 : Sending and Retrieving Messages from Firebase using BLOC
In the last post we finished our contacts page. The next thing a user would do from the contacts page, is to click on the Contact, which would open the Chat Page. We already built the UI for the Chat Page few weeks ago. Today we’ll be writing the Business logic and the Providers to handle the sending and receiving of the messages to and from Firebase.
Generate the Bloc
Generate your bloc using Bloc Generator at blocs/chats . Something like this.
Our Chat Screen can have atmost three states. The InitialState, the FetchedMessagesState (when the messages are retrieved from Firebase) and ErrorState in case of any errors.
And the possible events can be FetchMessagesEvent (triggered when chat opens), ReceivedMessagesEvent (triggered when messages stream has any updates to deliver), SendTextMessageEvent (used to send a normal text message). PickedAttachmentEvent ( when the user has picked a attachment as a message).
We have talked about the States and the Events but the most important part of the Chat Screen are the messages. We can have 4 types of messages in our case.
- TextMessage
- ImageMessage
- VideoMessage
- FileMessage
To simplify and to make the process of adding more message types in the future(if required) we’ll abstract out the core properties and methods of our Message class. All the children types should then inherit and implement these properties and methods.
Firestore Structure
Our Firestore DB for the chats will look something like this.
Here, chat1 is a chat between the participants ‘ron’ and ‘smith’. All the messages in this chat will be part of the messages Collection under chat. So we’ll need to read the messages from /chats/chat_id/messages . We also need to retrieve the last message from the chat, (for the homepage view where just the last message is shown). This technique of improving read speeds by duplicating data is called Fanning, in NOSQL Databases.
Also add these rules to your Firestore DB rules.
match /chats/{document=**} {
function isSignedIn() {
return request.auth.uid != null;
}
allow create, read, update, delete : if isSignedIn();
}At the end of the series we’ll write secure rules for all the reads and writes. Right now I’m just allowing r/w to anyone who is signed in.
The ChatProvider
The job of the ChatProvider will be to facilitate reading and writing of the messages from the /chats Collection.
Like in previous posts, I’ve avoided creating a concrete implementation and created a BaseChatProvider which contains the declaration of all the methods. Architecturing like this helps in future modifications.
Check the Code Changes section or this if you want to have a look at how the BaseProviders and the Repositories are structured.
The Bloc
And lastly we’ll write the ChatBloc for mapping Events to States.
For sending normal text messages we’re just using the chatRepository.sendMessage method. For receiving them we’re using a Stream. Since we do not want our Stream to be directly communicating with the UI but instead go through a Bloc State, we’re triggering another Event which yields a State with updated data.
Do notice that in the mapPickedAttachmentEventToState method we aren’t yet checking for the FileType and directly assuming that it is a VideoMessage . We’ll fix this when we wire our Bloc with the UI and also iron out the other little things.
I’ve also updated the tests for the ContactsBloc post, so do check them out, it includes testing streams which is something I haven’t covered yet in my blog. So that’s it, we’re done for the day!
Hit me up in the comments below or Twitter or Linkedin or Instagram if you have any queries or suggestions or just for a casual tech talk maybe? See you guys in the next one!
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!
