•Updated: Jun 14, 2021
•Published: Feb 26, 2021
In this tutorial, we’ll build a functional clone of iMessage using Stream Chat Flutter SDK. Building a chat in your app at scale is not an easy task; but in this tutorial, you’ll get a chat experience up and running in roughly 20 minutes! You can follow this tutorial without a deep knowledge of Flutter, a basic understanding of the layout system, Row and Column, will do.
There are three ways to use our SDKs. Depending on how much abstraction you want.
- stream_chat: low level dart client, which is a thin wrapper around our REST API
- stream_chat_flutter: an already made UI kit entirely customisable. See how Nash uses this package in this video
- stream_chat_flutter_core: expose BLoCs and builders to avoid implementing stream builders yourself
Since we are trying to reproduce the look and feel of an app, in this tutorial we'll use the way that give us the most degrees of liberty for customization:
stream_chat_flutter_core. Also, it is also worth to note that to stay consistent with stream_chat_flutter package we'll keep the naming convention the same for widgets.
If you get lost during this tutorial, you can check:
- The iMessage Clone GitHub repo
- The Stream Chat Flutter Tutorial
Let’s get started! 🚀
To proceed with this tutorial, you'll need the following:
- Flutter SDK
- A Stream account. You can create one here if you don’t have one.
- An Code editor such as Visual Studio Code
Install the sdk in
dependencies: stream_chat_flutter: ^1.3.0-beta
Don't forget to tap in your terminal
flutter packages getto download the depedencies. But usually, if you use vscode they will be downloaded on save or when you run your code.
Let's first build the static views then sprinkle 🪄 some Stream Flutter SDK magic here and there.
We'll first build the list view listing all conversations then the detailed view of those conversations.
Let's start with showing up a preview of the conversations
In each item of the list we need to display three things:
- the contact that sent the message, including its avatar and name
- a preview of the message. If there is a media, show a little emoji indicating if it is an image or a video (yes our product support all of that 😉)
- the day of the week and the hour at which the message was received. If it was more than a week ago we'll have to change the format for a nicer UX.
Since iMessage is an iPhone app we'll use Cupertino Widgets, which are high-fidelity widgets for current iOS design system.
Make it interactive
To make our ChannelPreview widget interactive, we use GestureDetector, among other things this widget is used for handling taps.
Formating dates 📅
To format the date we'll use the intl package is usually used to deal with internationalized/localized messages, date and number formatting and parsing, bi-directional text, and other internationalization issues. Let's define these utility functions in utils.dart
To build our listview we'll use a Sliver widget. A sliver is just a portion of a scrollable area. Let's call our widget
ChannelListView because in Stream, since we have different use cases such as livestream, customer support etc conversations happen in channels.
Let's add a nice AppBar and wrap this in a widget that we call
ChatLoader don't worry about channels parameters we'll explain later where does it come from.
Now that we have our listview displaying a preview of ours conversations. We need to navigate to individual items. Let's call those items that hold each conversation
MessagePage, since it displays messages. We'll need a navigation bar to display the contact with (avatar and name) whom we are having the discussion with and display the list of the messages. Let's call it
MessageListView. Again, forget about messages parameter here. We'll see how we can add them with our Chat SDK.
MessageListView we'll group messages by day like in the real app and change the color and display based on whether this is a message we sent or received. We'll need to "draw" a chat bubble. We'll also need an input to send those messages and attach medias to them.
Group Messages by Date
To group messages by day we use the function groupBy to group the elements in values by the value returned by key, from the collection package.
Then it's a matter of getting the date using
entries[index].key and the list of messages
entries[index].value and wrap this in a ListView builder as follow.
At the end, this is what the
MessageListView widget will look like:
MessageWidget we want to check attachment type type, if it is an image or video, and display it accordingly
To draw our bubble we use a CustomPainter, which is a widget used to draw custom shapes and paths. The api surface is a bit like html canvas (if you are familiar with it). Let's simply call this widget
ChatBubblethat takes into parameters the color and the alignment. We'll display the bubble differently according to alignment.
In our view now that we have displayed the messages we need to send a message. To do so, let's call our widget
MessageInput. We use the ImagePicker plugin to take a photo from the gallery and uploading it along with our message.
Spicing it up with Stream Chat SDK 🌶️
Now that everything is setup. "How do we make an actual chat?" You may ask.
Well that's simple, let's initialize our SDK and
runApp to run our top widget
IMessage that we haven't defined yet, until now.
For this to work you'll need an api key, and an user token that you can get in your dashboard.
Remember when we set up our ChatLoader ?
ChannelListView was taking the parameter
channels but we did'nt explained where does it come from. Now is the time to add the missing piece. Let me present you, your two new best friends: ChannelsBloc and ChannelListCore.
ChannelListCore exposes builders to let you customize how to handle errors, loading progress, but most importantly it exposes
List<Channel>. It also has options for filters, sorting, and pagination.
ChatLoader we'll look like this:
LazyLoadScrollView, is a wrapper around a Scrollable which triggers onEndOfPage/onStartOfPage when the Scrollable reaches to the start or end of the view extent. It exposes callbacks like onRefresh which comes handy in our case with
Same patterns go for MessagePage, thanks to MessageListCore you can have access to different builders, including the one exposing
This concludes part one of our tutorial on building a iMessage clone using Stream’s Flutter Chat SDK. I hope you found this tutorial useful, and I look forward to hearing your feedback.
In a next article – which will be published later – we will cover how to implement a feature such as
Search to search through messages.