Compose Chat Messaging Tutorial
Build an In-App Chat Feature with Jetpack Compose
This tutorial shows you how to get up and running with the Stream Jetpack Compose Chat SDK to add rich messaging features to your app.
We'll start with a super quick and simple integration, and then look at some of the flexibility and customization that the Compose SDK offers.




Creating a Project
Confused about "Creating a Project"?
Let us know how we can improve our documentation:
If you're not using Compose yet, check out our Android tutorial with XML layouts!
The completed app for each step of the tutorial is available on GitHub.
To get started with the Jetpack Compose version of the Android Chat SDK, open Android Studio (Arctic Fox or later) and create a new project.
- Select the
Empty Compose Activity
template - Name the project
ChatTutorial
- Set the package name to
com.example.chattutorial
Once you create and load the project, you need to add appropriate dependencies for Jetpack Compose.
Our SDKs are available from MavenCentral. Update your repositories in the settings.gradle
file like so:
Go to the project level build.gradle
file, and make sure that you're using Compose 1.2.1
, Compose Compiler 1.3.0
, and Kotlin 1.7.10
(or newer, compatible versions).
Depending on which version of Android Studio was used to create the project, the contents may look slightly differently:
Arctic Fox
Bumblebee and up
Next, you're going to add the Stream Chat Compose SDK to the project's dependencies. You'll also add a dependency on material-icons-extended
, as you'll use an icon provided there for customization in later steps.
Open up the app module's build.gradle
file and add the following two dependencies:
Next, make sure your project uses the correct Compose Compiler version on the build.gradle
file:
After you edit your Gradle files, make sure to sync the project (Android Studio will prompt you for this) with the new changes.
You're now ready to use our SDK to build your first screen - the Channels Screen!
Displaying the Channels Screen
Confused about "Displaying the Channels Screen"?
Let us know how we can improve our documentation:
Stream provides a low-level client, an offline support library, and convenient Jetpack Compose UI components to help you easily integrate messaging in your app. In this section, you'll be using the components to quickly display a channel list.
Open MainActivity
and replace the file's contents with the following code:
Let's have a quick look at the source code shown above:
- You create a
StreamOfflinePluginFactory
to provide offline support. TheOfflinePlugin
class employs a new caching mechanism powered by side-effects we applied to ChatClient functions. - You create a connection to Stream by initializing the
ChatClient
using an API key. This key points to a tutorial environment, but you can sign up for a free Chat trial to get your own later. Next, we add theofflinePluginFactory
to theChatClient
withwithPlugin
method for providing offline storage capabilities. For a production app, we recommend initializing thisChatClient
in your Application class. - You create a
User
instance and pass it to theChatClient
'sconnectUser
method, along with a pre-generated user token, in order to authenticate the user. In a real-world application, your authentication backend would generate such a token at login / signup and hand it over to the mobile app. For more information, see the Tokens & Authentication page. - You call the
ChannelScreen
composable function to render the entireChannelScreen
, and wrap it in our theme which provides styling options. You also pass in the app name as the screen's title, and anonBackPressed
listener.
Composable UI Components rely on a ChatTheme
being present somewhere above them in the UI hierarchy. Make sure you add this wrapper whenever you're using the components of the Chat SDK. Learn more in the ChatTheme documentation.
Build and run your application - you should see the channel screen interface shown on the right. Notice how easy it was to build a fully-functional screen with Compose!
Internally, the ChannelsScreen
uses these smaller components:
ChannelListHeader
: Displays information about the user, app and exposes a header action.SearchInput
: Displays an input field, to query items. Exposes value change handlers, to query new items from the API.ChannelList
: Displays a list ofChannel
items in a paginated list and exposes single and long tap actions on items.
If you want to customize what this screen looks like, or how it behaves, you can build it from these individual components. See the Component Architecture page of the documentation for more details.
Now that you can display channels, let's open up one of them and start chatting!
Creating a Chat Experience
Confused about "Creating a Chat Experience"?
Let us know how we can improve our documentation:
To start chatting, you need to build another screen - the Messages Screen.
Create a new Empty Activity (New -> Activity -> Empty Compose Activity) and name it MessagesActivity
.
Make sure that MessagesActivity
is added to your manifest. Android Studio does this automatically if you use the wizard to create the Activity, but you'll need to add it yourself if you manually created the Activity class.
After creating the Activity, add the following attribute to the MessagesActivity
entry in your manifest:
This will make sure the Activity
adjusts properly when you focus the input field.
Creating a new Activity
might change your Kotlin version in your project level build.gradle
file, which can lead to errors if the new version is incompatible with the Compose version used. Double-check your versions in this file after creating MessagesActivity
.
Also, make sure to check that your Kotlin version is still 1.7.10
:
Arctic Fox
Bumblebee and up
Android Studio can sometimes update the version when adding new Activities to the project and this can cause build time issues.
Next, replace the code in MessagesActivity
with the following:
Let's review what's going on in this snippet:
- You load the
channelId
from the Intent extras. If there is no channel ID, you can't show messages, so you finish theActivity
and return. Otherwise, you can proceed to render the UI. - Similar to the
ChannelsScreen
, theMessagesScreen
component sets up everything for you to show a list of messages and build a Chat experience. Note how this screen's composable should also wrapped inChatTheme
. - Set up a helper function to build an
Intent
for thisActivity
, that populates the arguments with the channel ID.
Lastly, you want to launch MessagesActivity
when you tap a channel in the channel list. Open MainActivity
and replace the TODO within onItemClick
with the following:
Run the application and tap on a channel: you'll now see the chat interface shown on the right.
The MessagesScreen
component is the second screen component in the SDK, out of the box it provides you with the following features:
- Header: Displays a back button, the name of the channel or thread and a channel avatar.
- Messages: Shows a paginated list of messages if the data is available, otherwise displays the correct empty or loading state. Sets up action handlers and displays a button for quick scroll to bottom action.
- Message composer: Handles message input, attachments and message actions like editing, replying and more.
- Attachment picker: Allows the user to select images, files and media capture.
- Message options: Shown when the user selects the message by long tapping. Allows the user to react to messages and perform different actions such as deleting, editing, replying, starting a thread and more.
- Reactions menu: Shown when the user taps on a reaction to a message. Displays all of the reactions left on the message along with the option to add or change yours.
You can explore all of these components individually, combine them to your requirements, and explore the Compose UI Components documentation to see how they behave and how you can customize them.
Chat Features
Confused about "Chat Features"?
Let us know how we can improve our documentation:
Congrats on getting your chat experience up and running! Stream Chat provides you with all the features you need to build an engaging messaging experience:
- Offline support: send messages, edit messages and send reactions while offline
- Reactions: long-press on a message to add a reaction
- Attachments: use the paperclip button in
MessageComposer
to attach images and files - Edit message: long-press on your message for message options, including editing
- Threads: start message threads to reply to any message
- Replies: quote and reply to a message
You should also notice that the chat loads very quickly. Like - super fast! Stream’s API is powered by Go, RocksDB and Raft. The API tends to respond in less than 10ms and powers activity feeds and chat for over a billion end users.
Some features are hard to see in action with just one user online. You can open the same channel on the web and try user-to-user interactions like reactions and threads.
Chat Message Customization
Confused about "Chat Message Customization"?
Let us know how we can improve our documentation:
You now have a fully functional mobile chat interface. Not bad for a couple minutes of work! Maybe you'd like to change things up a bit though? No problem! Here are a few ways you can customize your Chat app experience:
- Change the default theming and styling options in
ChatTheme
, such as shapes, typography or colors. - Combine our bound and stateless components with your own UI to create a custom screen.
- Use our components and customize their content.
- Build your custom UI components and connect them to the state exposed by our
ViewModel
s. - Use the low-level client to build custom behavior, based on the available state.
Let's combine the first three approaches to customize the Messages screen. The remaining two options are great if you want to build complex features in your Chat app, but we'll skip those for now.
Changing MessagesScreen Theming
To change the theming of all the components wrapped by ChatTheme
, all you have to do is override its default parameters. Let's do that, with the shapes
parameter. Change the setContent()
code in MessagesActivity.kt
to the following:
This will require the following new imports at the top of the file:
With this small change, you can override the default shapes used in our Compose UI Components.
You made the following changes:
- You made the
inputField
rectangular, instead of having rounded corners. - The owned and other people's messages will be rounded on all corners, regardless of their position in a message group
- Avatars and attachments have rounded corners, of
8dp
and16dp
, respectively.
Notice how you changed the theme shapes
using copy()
. For ease of use, you can fetch the default theme values and use copy()
on the data class to change just the properties you want to customize.
If you build the app now and open the messages screen, you'll see how the messages are all rounded, the input is rectangular and the avatars are now a squircle! That was easy!
Combining Components
The next step of customization is combining our bound and stateless components instead of using the screen components. This gives you more control over which components you use and render and what the behavior is when tapping on items, selecting messages and more.
First, start off by building the required ViewModel
s for the messaging experience, at the top of the MessagesActivity
class:
This requires the following new imports:
The next step is to build custom Messages screen UI. Replace the setContent()
call with the following:
Now, instead of showing the MessagesScreen
, you'll show your custom UI when opening a Channel
. Time to add the MyCustomUi()
function:
Here's a list of the new imports to add:
Remember, the completed app for each step of the tutorial is available on GitHub. Checking the repo is useful if you get stuck with finding the correct imports.
There is a lot going on here, so let's review the steps in the code:
- You first load the required data, using the
ViewModel
s you've provided. - You define the root components as
Box
andScaffold
. TheBox
allows you to show things on top of each other, such as dialogs, overlays, bottom bars and more. TheScaffold
helps you build a screen where you have a top bar, content and a bottom bar. - You show the
MessageComposer()
as thebottomBar
. This will let you send text messages and attachments. - You show the
MessageList
as thecontent
. You also connect theonThreadClick
handler to the requiredViewModel
s. - Lastly, you show the
SelectedMessageOverlay
, if there is aselectedMessage
within thelistViewModel
.
If you build and run the app again, you'll see the following screen. Notice how the header is missing, because you didn't add it to the topBar
of the Scaffold
.
This shows you how you can easily add or remove elements from the screen, or even replace them with custom components you build.
Customize Component Content
Finally, let's build a customized MessageComposer
. You'll use the MessageComposer
, but you'll change its internal content by overriding the integrations
and input
composable function parameters.
Let's replace the MessageComposer()
call in MyCustomUi()
with a new MyCustomComposer()
function:
This change will replace the default composer with your custom one.
Now create the MyCustomComposer()
function with the following code:
Here are the new imports required:
Again, there's a few things going on here:
- You call the
MessageComposer
and pass in the required parameters, as well as some customization using amodifier
. - You override the default
integrations
with an empty function, effectively removing the integrations. - You override the
input
composable function with a customMessageInput
. Again, you pass in the required parameters and add more styling options through themodifier
. - Within the
MessageInput
, you override thelabel
composable function, to show a custom label with anIcon
and aText
element.
This shows how you can use our Compose UI Components to combine them according to your needs, as well as customize their internal UI.
Build and run the app again and you'll now see a custom MessageComposer
, it was that simple!
That didn't take a lot of work, but you've managed to customize the Messages screen in three different ways!
Congratulations!
Confused about "Congratulations!"?
Let us know how we can improve our documentation:
You've now built a messaging app using Stream's Compose Chat SDK.
Quick reminder: you can find the tutorial repo on GitHub. This contains the completed code for each step of this tutorial for you to try out.
As the next step of exploring the SDK, take a look at Compose UI Components documentation. This showcases all of our available components in detail, and explains many of the design principles and customization options of the Compose SDK.
Our Compose UI Components are still in beta, and we want to hear your feedback on using them! You can reach us easily on Twitter and on GitHub.
To use the Stream SDK in your app, you should get your own account and API key. You can get these by signing up for a free Chat trial. If you're building a hobby project or working for a small company, you might also be eligible for a Maker Account, which lets you use the Stream Chat API for free indefinitely.
Final Thoughts
In this chat app tutorial we built a fully functioning Compose messaging app with our Compose SDK component library. We also showed how easy it is to customize the behavior and the style of the Compose chat app components with minimal code changes.
Both the chat SDK for Compose and the API have plenty more features available to support more advanced use-cases such as push notifications, content moderation, rich messages and more. Please check out our Android tutorial too. If you want some inspiration for your app, download our free chat interface UI kit.
Give us Feedback!
Did you find this tutorial helpful in getting you up and running with Compose for adding chat to your project? Either good or bad, we’re looking for your honest feedback so we can improve.
We’ve been going at lightspeed to build more communication functionality into Kiddom. The only way to achieve this in four months was to do a chat integration with Stream because we needed to do it reliably and at scale.
Nick Chen
Head of Product, Kiddom