Custom Composer

The MessageComposer component can be customized at two levels:

  1. Parameters: Adjust the existing UI using MessageComposer parameters like modifier, input, and onAttachmentsClick.
  2. Component Factory: Replace the leading/trailing content, input field, or attachment picker UI by overriding methods in ChatComponentFactory.

For most customizations, the Component Factory approach is recommended. See the Component Factory page for details on overriding MessageComposerLeadingContent(), MessageComposerTrailingContent(), and other composer slots.

Parameter-Level Customization

If you need to customize the composer within a custom screen (without using ChannelScreen), create it directly with MessageComposerViewModel:

@Composable
private fun CustomMessageComposer(
    composerViewModel: MessageComposerViewModel,
    attachmentsPickerViewModel: AttachmentsPickerViewModel,
) {
    MessageComposer(
        viewModel = composerViewModel,
        modifier = Modifier
            .fillMaxWidth()
            .wrapContentHeight(),
        input = { composerState ->
            MessageInput(
                messageComposerState = composerState,
                onValueChange = { composerViewModel.setMessageInput(it) },
                onAttachmentRemoved = { composerViewModel.removeAttachment(it) },
                modifier = Modifier
                    .padding(horizontal = 10.dp)
                    .align(Alignment.CenterVertically),
            )
        },
        onAttachmentsClick = { attachmentsPickerViewModel.setPickerVisible(true) },
    )
}

The input parameter gives you access to MessageComposerState, which contains the current message text, attachments, and other state. You can use the built-in MessageInput component with custom modifiers, or replace it entirely with your own composable.

Integrating in a Custom Screen

When building a custom messages screen, wire the composer alongside the message list and attachment picker:

@Composable
fun CustomScreen(cid: String, onBackClick: () -> Unit = {}) {
    val viewModelFactory = ChannelViewModelFactory(LocalContext.current, channelId = cid)
    val listViewModel = viewModel(modelClass = MessageListViewModel::class.java, factory = viewModelFactory)
    val composerViewModel = viewModel(modelClass = MessageComposerViewModel::class.java, factory = viewModelFactory)
    val attachmentsPickerViewModel = viewModel(modelClass = AttachmentsPickerViewModel::class.java, factory = viewModelFactory)

    Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.BottomCenter) {
        Scaffold(
            topBar = { /* ChannelHeader */ },
            bottomBar = {
                CustomMessageComposer(
                    composerViewModel = composerViewModel,
                    attachmentsPickerViewModel = attachmentsPickerViewModel,
                )
            },
            content = { /* MessageList */ },
        )

        if (attachmentsPickerViewModel.isPickerVisible) {
            AttachmentPicker(attachmentsPickerViewModel = attachmentsPickerViewModel)
        }
    }
}
Add Chat to my app: getstream.io/SKILL.md

The fastest way to build with Stream. Start a new project or improve an existing one. Full CLI and documentation integration out of the box.


Ask your agent:

/stream Build me a Social App with Feeds and Moderation.
/stream Any livestream calls running?
/stream Chat Android v7: <Your Question>