We all have probably written chat apps, multiple times in our lives, and we all know that IT TAKES TIME! So I always wondered is there a way to make totally custom and beautiful chat app by using some lib/service that does all the heavy lifting for me.
Fully blown chat app in a matter of hours?
Are you kidding me? :D
After 6–7 hours of trying out their lib, this is how it came out:
You can use their predefined Widgets for channel items(Inbox) as well as message(channel or group) thread and message input. But, your product manager would probably not be OK with that :) Chat, as well as every part of the app, has to follow certain style, and that is the objective of this article:
Creating custom widgets that can be incorporated in Stream’s chat library :)
Channel is a group of 1+ users, by default, so you can get very creative.
More info about creating different types of channels can be found *HERE*.
For the sake of this example, I’ve created ChatData singleton where I create a client and 2 users, that can be found *HERE*. Most of this stuff should be on your back-end side.
This is where we start from:
StreamChat widget needs to be as up in the tree as possible, ‘cos it will hold StreamChatClient(used for handling all the api calls, websocket connections etc) and StreamChatThemeData(bunch of styles, colors, dimensions that can be used to change the appearance of your app, but still keeping the most of the default design).
My streamChatThemeData looks like this:
It’s pretty self explanatory.
Now, when we have the core structure set, let’s design :)
List of channels & channel item
ChannelsBloc is a Widget that takes care (holds instance and performs operations) on list of channels, channel subscriptions, querying, sorting etc.
ChannelsBloc needs to be below the StreamChat Widget in the element tree, ‘cos it fetches StreamChatClient from it.
ChannelsBloc Widget is always accessible via current context(if your context is below ChannelsBloc in the element tree!):
ChannelsBlocState channelsBloc = ChannelsBloc.of(context);
This finds nearest ChannelsBlocState up in the element tree, so you could access your list of channels, their stream etc.
ChannelListView is a Stream’s Flutter package Widget used for displaying channel(group) items. It deals out of the box with pagination, filtering, loading etc.There you can add your custom item widget, custom separators, and lots more.
All data needed to draw item(messages, title, last message time, etc) can be fetched thru the channel instance, that we get via builder:
Now we create totally custom channel item:
What we basically do here is - when a new unread message arrives, we cross fade initial view, into a colored one. StreamBuilder listens to the changes in the unreadCountStream, and in order to unread count, we call method channelView(…,true/false).
That parameter determines the UI of the item. If we have new(unread) messages it’s colored, with light text, and if not, it’s plain white, with dark text.
It looks like this in slow motion:
Complete code for the ListOfChannels(list of channel items) can be found *HERE*.
Channel(Group or Message) Thread
When user clicks on channel item, we open a new screen with messages - ChannelThread Widget.
Same as ChannelsBloc, here we have StreamChannel Widget, which is just a holder of channel instance. By specifying:
somewhere within our ChannelThread Widget, we fetch nearest channel UP in the element tree(the one from StreamChannel’s widget).
Complete ChannelThread(header, list of messages and input field) code can be found *HERE*, but’s lets go thru the code:
is just an AppBar in the Scaffold, that contains of the BackButton(does only the popping), ChannelName(package component that fetches channel from the element tree from the StreamChannel widget we defined above it) and a ChannelImage(also package component which fetches the channel in the same way as ChannelName), but we added some shadow to it :)
The interesting thing about ChannelImage widget, is that it has optional parameter channel, so if it’s not provided in the element tree, it can be added manually in the constructor and used like that:
…so, be sure to check how they implemented those widgets. They can be used in many different ways :)
List Of Messages
MessageListView is also a package Widget that requires StreamChannel Widget UP in the element tree to be able to display messages from it!!!
Now, we’ll see how we can implement custom message bubble:
Here we make comparison of current user id and message user id. If the id is the same, that means that our current user has created this message, and we’ll place its message right on the screen:
The same logic is used for choosing bubble color and layout(if message user is current user, we first display text, then avatar, and if not, it’s reversed).
..and then rowItems are place within a Row Widget:
MessageInput is also a package Widget that can be adjusted with the help of StreamChatTheme we defined in the StreamChat Widget in the root of our app.
There you can change input text color, size, background color, input field borders and radius.. and lots more.
I really had fun using Stream’s chat package(service)!
Try it out :)
If your company has five or less team members, less than $10k in monthly revenue and have taken less than $100k in funding, Stream is free/gratis/complimentary.
Complete code can be found in the CANDY LAND repo.
Happy Fluttering 💙