Channel

MessagesScreen is a ready-to-use chat screen that handles the entire messaging experience. Use it when you want a full-featured chat UI with minimal setup.

Included features:

  • Message list: Displays messages with automatic pagination, date separators, loading states, empty states, and a scroll-to-bottom button for new messages
  • Message composer: Full-featured input with text formatting, attachment buttons, voice recording, edit mode, and reply-to-message functionality
  • Attachment picker: Bottom sheet for selecting images from gallery, capturing photos/videos, or picking files
  • Message actions: Long-press menu with options for reactions, reply, thread reply, copy, edit, delete, pin, and flag
  • Reactions: Emoji picker for adding reactions and display of reaction counts on messages
  • Thread support: Inline thread replies with automatic navigation to thread view
  • Typing indicators: Shows when other users are typing in the channel
  • Read receipts: Displays read status for sent messages

If you need more control over the layout or want to add custom UI elements, consider using the individual bound components like MessageList and MessageComposer instead.

Usage

The benefit of a screen component solution is easy integration. All you need to do to integrate MessagesScreen in your app is to call it within setContent() in your Activity or Fragment and pass in the MessagesViewModelFactory with your channelId:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // Load the ID of the channel you've opened
    val channelId = "messaging:123"

    setContent {
        ChatTheme {
            MessagesScreen(
                viewModelFactory = MessagesViewModelFactory(
                    context = this,
                    channelId = channelId
                )
            )
        }
    }
}

This small snippet of code will render the UI shown below:

LightDark
Default MessagesScreen component
Default MessagesScreen component dark

Next, learn more about handling screen actions.

Handling Actions

The MessagesScreen component exposes several actions for handling user interactions:

@Composable
fun MessagesScreen(
    ..., // State and customization
    onBackPressed: () -> Unit = {},
    onHeaderTitleClick: (channel: Channel) -> Unit = {},
    onChannelAvatarClick: (() -> Unit)? = null,
    onComposerLinkPreviewClick: ((LinkPreview) -> Unit)? = null,
    onMessageLinkClick: ((Message, String) -> Unit)? = null,
    onUserAvatarClick: ((User) -> Unit)? = null,
    onUserMentionClick: (User) -> Unit = {},
)
  • onBackPressed: Called when the user taps on the header back button.
  • onHeaderTitleClick: Called when the user taps on the header title. Useful for showing the channel information.
  • onChannelAvatarClick: Called when the user taps on the channel avatar. Can also be used to show more channel information.
  • onComposerLinkPreviewClick: Called when the user taps on a link preview in the message composer. Useful for opening the link in a browser or custom handler.
  • onMessageLinkClick: Called when the user taps on a link within a message. Receives both the Message and the URL String that was clicked.
  • onUserAvatarClick: Called when the user taps on another user's avatar in the message list. Useful for showing user profile information.
  • onUserMentionClick: Called when the user taps on a user mention (e.g., @username) within a message. Useful for navigating to the mentioned user's profile.

Here's an example of setting up custom behavior:

MessagesScreen(
    viewModelFactory = viewModelFactory,
    onBackPressed = { finish() },
    onHeaderTitleClick = { channel ->
        // Show channel info
    },
    onChannelAvatarClick = {
        // Show channel details
    },
    onComposerLinkPreviewClick = { linkPreview ->
        // Open link preview URL
        openUrl(linkPreview.originUrl)
    },
    onMessageLinkClick = { message, url ->
        // Handle link click within a message
        openUrl(url)
    },
    onUserAvatarClick = { user ->
        // Navigate to user profile
        navigateToUserProfile(user.id)
    },
    onUserMentionClick = { user ->
        // Navigate to mentioned user's profile
        navigateToUserProfile(user.id)
    }
)

Customization

The MessagesScreen component offers several customization options:

@Composable
fun MessagesScreen(
    viewModelFactory: MessagesViewModelFactory,
    showHeader: Boolean = true,
    reactionSorting: ReactionSorting = ReactionSortingByFirstReactionAt,
    skipPushNotification: Boolean = false,
    skipEnrichUrl: Boolean = false,
    showAnonymousAvatar: Boolean = false,
    verticalArrangement: Arrangement.Vertical = Arrangement.Top,
    topBarContent: @Composable (BackAction) -> Unit = { ... },
    bottomBarContent: @Composable () -> Unit = { ... },
    ... // action handlers
)
  • viewModelFactory: The factory that you build yourself. This lets you control not just the way the ViewModels are built, but also their lifecycle, as you can share them between components. This requires of you to provide a channelId in order to power the screen and show data, but it also allows you to customize the behavior of the screen through various parameters.
  • showHeader: Controls whether the messages header is shown or not.
  • reactionSorting: Defines how reactions are sorted when displayed. Defaults to ReactionSortingByFirstReactionAt, which sorts reactions by the time the first reaction of each type was added.
  • skipPushNotification: When set to true, sending messages from this screen will not trigger push notifications to other users. Defaults to false.
  • skipEnrichUrl: When set to true, URLs in messages will not be enriched with link previews. Defaults to false.
  • showAnonymousAvatar: When set to true, displays avatars for anonymous users in poll comments. Defaults to false.
  • verticalArrangement: Controls the vertical arrangement of messages in the list. Defaults to Arrangement.Top. You can use other arrangements like Arrangement.Bottom or Arrangement.SpaceBetween to change how messages are positioned.
  • topBarContent: A composable slot for customizing the top bar/header content. Receives a BackAction parameter that you can use to handle back navigation. Use this to completely replace the default header with your own implementation.
  • bottomBarContent: A composable slot for customizing the bottom bar content, which by default contains the message composer. Use this to replace or wrap the default composer with additional UI elements.

Hiding the Header

If you set showHeader to false you'll get the following UI:

LightDark
MessagesScreen without the MessageListHeader
MessagesScreen without the MessageListHeader Dark

As you can see, the header is removed from the screen, rendering only the list and the composer.

Customizing the Top Bar

You can provide a custom top bar using the topBarContent parameter:

MessagesScreen(
    viewModelFactory = viewModelFactory,
    topBarContent = { backAction ->
        // Custom header implementation
    }
)

Customizing the Bottom Bar

You can customize the bottom bar to add additional UI elements around the composer:

MessagesScreen(
    viewModelFactory = viewModelFactory,
    bottomBarContent = {
        // Custom bottomBar (composer) implementation
    }
)

Overriding the ViewModels

In case you want to control the logic when using the MessagesScreen, you can do so by providing a MessagesViewModelFactory that you use to build the respective ViewModels yourself.

Here's an example:

class MessagesActivity : ComponentActivity() {

    // 1
    private val factory by lazy {
        MessagesViewModelFactory(
            context = this,
            channelId = channelId,
            // Customization options
        )
    }

    // 2
    private val listViewModel by viewModels<MessageListViewModel>(factoryProducer = { factory })

    private val attachmentsPickerViewModel by viewModels<AttachmentsPickerViewModel>(factoryProducer = { factory })
    private val composerViewModel by viewModels<MessageComposerViewModel>(factoryProducer = { factory })

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            ChatTheme {
                MessagesScreen(
                    // 3
                    viewModelFactory = factory,
                    onBackPressed = { finish() },
                )
            }
        }
    }
}

There are a few steps here that allow you to override and control the ViewModels:

  1. You create the MessagesViewModelFactory yourself, which lets you describe the data and configuration used to build the ViewModels.
  2. You lazily create an instance of the required ViewModels. This means that you'll either build the ViewModel first and then pass it to the Compose component, or your Compose component will create the ViewModel and you'll get access to it here.
  3. You pass in the factory to the MessagesScreen, which allows this connection to happen.

The ViewModels should be the same and you should easily be able to react to things like item clicks, changes in the state and more.

MessagesViewModelFactory Configuration

The MessagesViewModelFactory provides extensive configuration options for customizing the behavior of the messages screen. Here's a complete overview of all available parameters:

MessagesViewModelFactory(
    // Required
    context = context,
    channelId = "messaging:123",

    // Navigation
    messageId = null,
    parentMessageId = null,

    // Message display
    messageLimit = 25,
    showSystemMessages = true,
    deletedMessageVisibility = DeletedMessageVisibility.ALWAYS_VISIBLE,
    messageFooterVisibility = MessageFooterVisibility.WithTimeDifference(),
    enforceUniqueReactions = true,

    // Separators and grouping
    dateSeparatorHandler = DateSeparatorHandler.getDefaultDateSeparatorHandler(),
    threadDateSeparatorHandler = DateSeparatorHandler.getDefaultThreadDateSeparatorHandler(),
    messagePositionHandler = MessagePositionHandler.defaultHandler(),

    // Composer settings
    maxAttachmentCount = 10,
    isComposerLinkPreviewEnabled = false,
    isComposerDraftMessageEnabled = false,

    // Thread settings
    showDateSeparatorInEmptyThread = false,
    showThreadSeparatorInEmptyThread = false,
    threadLoadOlderToNewer = false,

    // Features
    autoTranslationEnabled = false,
)

Required Parameters

  • context: Android context, used internally for clipboard operations and other system services.
  • channelId: The ID of the channel to display messages for.
  • messageId: When provided, the message list will automatically scroll to this message when opened. Useful for deep-linking to specific messages.
  • parentMessageId: When scrolling to a message that's inside a thread, provide the parent message ID to ensure proper navigation.

Message Display Parameters

  • messageLimit: Number of messages to load per page. Defaults to 25.
  • showSystemMessages: Whether to display system messages (e.g., "User joined the channel"). Defaults to true.
  • deletedMessageVisibility: Controls how deleted messages are displayed. Options:
    • DeletedMessageVisibility.ALWAYS_VISIBLE: Always show deleted message placeholders
    • DeletedMessageVisibility.VISIBLE_FOR_CURRENT_USER: Show only if deleted by current user
    • DeletedMessageVisibility.ALWAYS_HIDDEN: Never show deleted messages
  • messageFooterVisibility: Controls when message footers (timestamp, read status) are shown. Defaults to showing footers when there's a time difference between messages.
  • enforceUniqueReactions: When true, users can only add one reaction of each type per message. When false, users can add multiple reactions of the same type. Defaults to true.

Separator and Grouping Parameters

  • dateSeparatorHandler: Controls when date separators appear between messages. The default handler shows separators when messages are from different days.
  • threadDateSeparatorHandler: Same as above but for thread message lists.
  • messagePositionHandler: Determines message grouping and positioning (e.g., whether a message is the first, middle, or last in a group from the same user).

Composer Parameters

  • maxAttachmentCount: Maximum number of attachments allowed per message. Defaults to 10.
  • isComposerLinkPreviewEnabled: When true, URLs typed in the composer will show link previews. Defaults to false.
  • isComposerDraftMessageEnabled: When true, enables draft message support. Unsent messages are saved and restored. Defaults to false.

Thread Parameters

  • showDateSeparatorInEmptyThread: Whether to show a date separator in threads with no replies. Defaults to false.
  • showThreadSeparatorInEmptyThread: Whether to show the thread separator in threads with no replies. Defaults to false.
  • threadLoadOlderToNewer: Controls the load direction for thread messages. When false (default), newer messages load first. When true, older messages load first.

Feature Parameters

  • autoTranslationEnabled: When true, messages are automatically translated based on user language preferences. Defaults to false.

Advanced Parameters

These parameters allow injecting custom implementations for advanced use cases:

  • mediaRecorder: Custom StreamMediaRecorder for voice message recording.
  • userLookupHandler: Custom handler for user mention lookups in the composer.
  • clipboardHandler: Custom handler for clipboard operations (copy message).
  • fileToUriConverter: Custom function for converting files to URI strings.

In addition to the customization options above, you can achieve a unique look and feel by modifying ChatTheme parameters. For more information on how to do so read our Customizing Components page.