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

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

Creating a new Jetpack Compose project in Android Studio

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

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:

  1. You create a StreamOfflinePluginFactory to provide offline support. The OfflinePlugin class employs a new caching mechanism powered by side-effects we applied to ChatClient functions.
  2. 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 the offlinePluginFactory to the ChatClient with withPlugin method for providing offline storage capabilities. For a production app, we recommend initializing this ChatClient in your Application class.
  3. You create a User instance and pass it to the ChatClient's connectUser 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.
  4. You call the ChannelScreen composable function to render the entire ChannelScreen, and wrap it in our theme which provides styling options. You also pass in the app name as the screen's title, and an onBackPressed 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 of Channel 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.

The Channels Screen of the Compose SDK

Now that you can display channels, let's open up one of them and start chatting!

Creating a Chat Experience

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:

  1. You load the channelId from the Intent extras. If there is no channel ID, you can't show messages, so you finish the Activity and return. Otherwise, you can proceed to render the UI.
  2. Similar to the ChannelsScreen, the MessagesScreen 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 in ChatTheme.
  3. Set up a helper function to build an Intent for this Activity, 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 Messages Screen of the Compose SDK

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

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:

  1. Offline support: send messages, edit messages and send reactions while offline
  2. Reactions: long-press on a message to add a reaction
  3. Attachments: use the paperclip button in MessageComposer to attach images and files
  4. Edit message: long-press on your message for message options, including editing
  5. Threads: start message threads to reply to any message
  6. 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

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:

  1. Change the default theming and styling options in ChatTheme, such as shapes, typography or colors.
  2. Combine our bound and stateless components with your own UI to create a custom screen.
  3. Use our components and customize their content.
  4. Build your custom UI components and connect them to the state exposed by our ViewModels.
  5. 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 and 16dp, 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!

Messages Screen with custom shapes configured

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 ViewModels 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:

  1. You first load the required data, using the ViewModels you've provided.
  2. You define the root components as Box and Scaffold. The Box allows you to show things on top of each other, such as dialogs, overlays, bottom bars and more. The Scaffold helps you build a screen where you have a top bar, content and a bottom bar.
  3. You show the MessageComposer() as the bottomBar. This will let you send text messages and attachments.
  4. You show the MessageList as the content. You also connect the onThreadClick handler to the required ViewModels.
  5. Lastly, you show the SelectedMessageOverlay, if there is a selectedMessage within the listViewModel.

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.

Custom Messages Screen UI

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:

  1. You call the MessageComposer and pass in the required parameters, as well as some customization using a modifier.
  2. You override the default integrations with an empty function, effectively removing the integrations.
  3. You override the input composable function with a custom MessageInput. Again, you pass in the required parameters and add more styling options through the modifier.
  4. Within the MessageInput, you override the label composable function, to show a custom label with an Icon and a Text 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!

Messages Screen with a custom Composer built in Jetpack Compose

That didn't take a lot of work, but you've managed to customize the Messages screen in three different ways!


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.

Kiddom logo

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.

Head of Product, Kiddom profile picture

Nick Chen

Head of Product, Kiddom

Next Steps

Start your 30-Day Chat trial to try out all our Chat product has to offer. No commitment or credit card required. If you want a custom plan or have questions, we are eager to talk with you.

Activity Feeds

Build any kind of feed without the headache of scalability or reliability of your feeds.

Learn more about $ Activity Feeds


99.999% Uptime SLA, Industry leading compliance and security best practices.

Learn more about $ Enterprise