Chat Message List | Message Long-Press Menu |
![]() | ![]() |
MessageListController, which controls the message list from outside the component. All other logic is encapsulated in MessageList.Method | Parameter | Description |
MessageList | conversationID: String | Initializes the component and sets the conversation ID. For a one-to-one chat, the format is c2c_userID. For a group chat, the format is group_groupID. |
| modifier: Modifier | Jetpack Compose modifier for setting style, layout, behavior, and appearance. |
| config: MessageListConfigProtocol | Initializes the component and sets the message list style, action items, message filtering, and custom rendering rules. The default value is ChatMessageListConfig(). |
| customActions: List<MessageCustomAction> | Initializes the component and sets custom message menu options, which are appended after the default action items. |
| locateMessage: MessageInfo? | Initializes the component and sets the message to locate. Typically used to jump from the message search results page to the message page and locate the target message. |
| messageListViewModelFactory: MessageListViewModelFactory? | Factory for creating the internal MessageListViewModel. In most cases, you do not need to create it manually. The component already provides a default implementation. |
| controller: MessageListController? | Message list controller. Optional. Create it with rememberMessageListController() and pass it in to exit multi-select mode, send the typing status, and perform other actions from outside the component. See MessageListController below. |
| onTypingStatusChanged: (Boolean) -> Unit | Callback for changes to the other user's typing status. true means the other user is typing. It takes effect only in one-to-one chats when enableTyping in config is true. |
| onMultiSelectStateChanged: (Boolean) -> Unit | Callback for multi-select mode state changes. true means multi-select mode is entered, and false means multi-select mode is exited. |
| onUserClick: (String) -> Unit | Initializes the component and sets the handler for user avatar clicks. The callback parameter is the userID of the clicked user. |
rememberMessageListController() and pass it to the controller parameter of MessageList, you can control the message list from outside the component:Method/Property | Description |
exitMultiSelectMode() | Exits multi-select mode. |
isInMultiSelectMode(): Boolean | Whether the component is currently in multi-select mode. |
sendTypingStatus(isTyping: Boolean) | Sends your typing status to the other user. It is typically used with the typingStatusSender of MessageInput. It takes effect only in one-to-one chats when enableTyping is true. To reduce unnecessary messages, it is sent only when the other user has sent you a message within the last 30 seconds, and sending is rate-limited. |
typingState: StateFlow<Boolean> | Whether the other user is typing. This has the same effect as the onTypingStatusChanged callback. |
onUserClick in the initialization method.import androidx.compose.foundation.layout.Boximport androidx.compose.foundation.layout.fillMaxSizeimport androidx.compose.foundation.layout.systemBarsPaddingimport androidx.compose.runtime.Composableimport androidx.compose.ui.Modifierimport io.trtc.tuikit.chat.uikit.components.messagelist.ui.MessageList@Composablefun MessageListScreen(conversationID: String, onOpenUserProfile: (userID: String) -> Unit) {Box(modifier = Modifier.fillMaxSize().systemBarsPadding()) {MessageList(conversationID = conversationID) { userID ->// Handle the user avatar click eventonOpenUserProfile(userID)}}}
MessageListController so the two components work together: hide the input box in multi-select mode, press the back button to exit multi-select, and show a "The other user is typing" prompt.import androidx.activity.compose.BackHandlerimport androidx.compose.foundation.layout.Columnimport androidx.compose.foundation.layout.fillMaxSizeimport androidx.compose.foundation.layout.paddingimport androidx.compose.foundation.layout.systemBarsPaddingimport androidx.compose.material3.Textimport androidx.compose.runtime.Composableimport androidx.compose.runtime.getValueimport androidx.compose.runtime.mutableStateOfimport androidx.compose.runtime.rememberimport androidx.compose.runtime.setValueimport androidx.compose.ui.Modifierimport androidx.compose.ui.unit.dpimport io.trtc.tuikit.chat.uikit.components.messageinput.ui.MessageInputimport io.trtc.tuikit.chat.uikit.components.messagelist.ui.MessageListimport io.trtc.tuikit.chat.uikit.components.messagelist.ui.rememberMessageListController@Composablefun ChatScreen(conversationID: String) {val messageListController = rememberMessageListController()var isPeerTyping by remember { mutableStateOf(false) }var isMultiSelectMode by remember { mutableStateOf(false) }// In multi-select mode, press the back button to exit multi-selectBackHandler(enabled = isMultiSelectMode) {messageListController.exitMultiSelectMode()}Column(modifier = Modifier.fillMaxSize().systemBarsPadding()) {if (isPeerTyping) {Text(text = "The other user is typing...", modifier = Modifier.padding(16.dp))}MessageList(conversationID = conversationID,modifier = Modifier.weight(1f),controller = messageListController,onTypingStatusChanged = { isTyping -> isPeerTyping = isTyping },onMultiSelectStateChanged = { isMultiSelect -> isMultiSelectMode = isMultiSelect },onUserClick = { userID ->// Handle the user avatar click event})// Hide the input box in multi-select modeif (!isMultiSelectMode) {MessageInput(conversationID = conversationID,typingStatusSender = { isTyping ->// Send your typing status to the other usermessageListController.sendTypingStatus(isTyping)})}}}
ChatMessageListConfig to set the style. All constructor parameters of ChatMessageListConfig are optional named parameters. Properties that are not set use the default values:import androidx.compose.ui.unit.dpimport io.trtc.tuikit.chat.uikit.components.config.MessageAlignmentimport io.trtc.tuikit.chat.uikit.components.messagelist.config.ChatMessageListConfigimport io.trtc.tuikit.chat.uikit.components.messagelist.ui.MessageListval messageListConfig = ChatMessageListConfig(alignment = MessageAlignment.TWO_SIDED, // Messages you send are on the right, and messages from others are on the leftisShowRightAvatar = false, // Do not show the avatar on the right (messages you send)isShowTimeMessage = false, // Do not show time separatorscellSpacing = 16.dp, // Spacing between messageshorizontalPadding = 12.dp // Left and right margins of the message list)MessageList(conversationID = conversationID, config = messageListConfig) { userID ->// Handle the user avatar click event}
ChatMessageListConfig are described below. For switches related to action items, see "Custom Action Items":Property | Type | Description | Remarks |
alignment | MessageAlignment | Message alignment | MessageAlignment.LEFT: All messages are left-aligned.MessageAlignment.RIGHT: All messages are right-aligned.MessageAlignment.TWO_SIDED: Messages you send are right-aligned, and messages from others are left-aligned.The default value is TWO_SIDED. |
isShowTimeMessage | Boolean | Show time messages | Controls whether time separators are displayed between messages. The default value is true. |
isShowLeftAvatar | Boolean | Show left avatar | Controls whether messages displayed on the left show an avatar. With TWO_SIDED alignment, these are messages sent by others. The default value is true. |
isShowLeftNickname | Boolean | Show left nickname | Controls whether messages displayed on the left show a nickname. The default value is true. |
isShowRightAvatar | Boolean | Show right avatar | Controls whether messages displayed on the right show an avatar. With TWO_SIDED alignment, these are messages you send. The default value is true. |
isShowRightNickname | Boolean | Show right nickname | Controls whether messages displayed on the right show a nickname. The default value is true. |
cellSpacing | Dp | Message spacing | Controls the spacing between adjacent messages. The default value is 10.dp. |
isShowSystemMessage | Boolean | Show system messages | Controls whether group tip messages and recall notifications are displayed. The default value is true. |
isShowUnsupportMessage | Boolean | Show unsupported messages | Controls whether messages that the component cannot recognize, and can only display with the default style, are shown. The default value is true. |
horizontalPadding | Dp | Horizontal padding | Controls the left and right padding of the message list. The default value is 16.dp. |
avatarSpacing | Dp | Avatar spacing | Controls the spacing between the avatar and the message content. The default value is 8.dp. |
isShowReadReceipt | Boolean | Show read receipts | Controls whether the read status is shown for messages you send that require a read receipt. The message must be sent by a MessageInput with enableReadReceipt enabled. The default value is false. |
enableTyping | Boolean | Enable the typing status feature | Takes effect only in one-to-one chats and must be used with MessageListController. The default value is true. |
background | MessageListBackground? | Message list background | The default value is null. For details, see "List Background" below. |
defaultBubbleAppearance, leftBubbleAppearance, rightBubbleAppearance, ownBubbleAppearance, incomingBubbleAppearance | MessageBubbleAppearance? | Message bubble appearance | The default value is null for all of them. For details, see "Bubble Appearance" below. |
setBackgroundColor, setBackgroundGradient, and setBackgroundImage. clearBackground clears the setting:import io.trtc.tuikit.chat.uikit.components.messagelist.config.ChatMessageListConfigimport io.trtc.tuikit.chat.uikit.components.messagelist.config.MessageListBackground// Solid color backgroundval colorBackgroundConfig = ChatMessageListConfig().setBackgroundColor(0xFFF5F6F8.toInt())// Gradient background; at least 2 colors are requiredval gradientBackgroundConfig = ChatMessageListConfig().setBackgroundGradient(colors = intArrayOf(0xFFE8F0FE.toInt(), 0xFFFFFFFF.toInt()),direction = MessageListBackground.GradientDirection.TOP_BOTTOM)// Image backgroundval imageBackgroundConfig = ChatMessageListConfig().setBackgroundImage("https://example.com/chat_background.png")
setBackgroundImage is loaded by Coil. It supports network image URLs, local files, Uri, drawable resource IDs, and more.setDefaultBubbleAppearance, setLeftBubbleAppearance, setRightBubbleAppearance, setOwnBubbleAppearance, and setIncomingBubbleAppearance. The corresponding clearXxxBubbleAppearance methods clear the settings. Multiple appearances are merged field by field in the order "default → left/right → own/incoming". A field set in a later appearance overrides the same field in an earlier one. Corner radii are merged separately for each corner:import io.trtc.tuikit.chat.uikit.components.messagelist.config.ChatMessageListConfigimport io.trtc.tuikit.chat.uikit.components.messagelist.config.MessageBubbleAppearanceimport io.trtc.tuikit.chat.uikit.components.messagelist.config.MessageBubbleBackgroundimport io.trtc.tuikit.chat.uikit.components.messagelist.config.MessageBubbleCornerRadiusimport io.trtc.tuikit.chat.uikit.components.messagelist.config.MessageBubbleStrokeval bubbleConfig = ChatMessageListConfig()// Base appearance for all bubbles: 12 dp corner radius on all four corners.setDefaultBubbleAppearance(MessageBubbleAppearance(cornerRadius = MessageBubbleCornerRadius.all(12)))// Messages you send: gradient background, square bottom-right corner, and the other corners keep the base appearance.setOwnBubbleAppearance(MessageBubbleAppearance(background = MessageBubbleBackground.Gradient(colors = listOf(0xFF4F8BFF.toInt(), 0xFF1C66E5.toInt()),direction = MessageBubbleBackground.GradientDirection.LEFT_RIGHT),cornerRadius = MessageBubbleCornerRadius(bottomRight = 0)))// Received messages: white background, 1 dp stroke.setIncomingBubbleAppearance(MessageBubbleAppearance(background = MessageBubbleBackground.Color(0xFFFFFFFF.toInt()),stroke = MessageBubbleStroke(width = 1, color = 0xFFE5E6EB.toInt())))
MessageBubbleAppearance are described below. Size parameters are in dp, and color parameters are Int values in ARGB format:Field | Type | Description |
background | MessageBubbleBackground? | Bubble background. Supports Color (solid color), Gradient (gradient, at least 2 colors), DrawableResource (drawable resource ID), and DrawableValue (Drawable object). |
cornerRadius | MessageBubbleCornerRadius? | Corner radius of the four corners (topLeft, topRight, bottomRight, and bottomLeft). You can set them all at once with MessageBubbleCornerRadius.all(radius). |
stroke | MessageBubbleStroke? | Stroke width and color. |
contentInsets | MessageBubbleInsets? | Padding between the bubble and the message content, added on top of the message content's own margins. You can create it quickly with MessageBubbleInsets.all(value) or MessageBubbleInsets.symmetric(horizontal, vertical). |
minimumSize | MessageBubbleSize? | Minimum width and minimum height of the bubble. |
renderConfig.useDefaultBubble is true. Image, video, and emoji messages do not have a bubble by default. They are wrapped in a default bubble only when they have a reaction.isSupportXxx switches in config to control whether action items are displayed:import io.trtc.tuikit.chat.uikit.components.messagelist.config.ChatMessageListConfigimport io.trtc.tuikit.chat.uikit.components.messagelist.ui.MessageListMessageList(conversationID = conversationID,config = ChatMessageListConfig(isSupportCopy = false) // Do not show "Copy") { userID ->// Handle the user avatar click event}
Switch | Action ID | Description |
isSupportCopy | MessageActionIDs.COPY | Copy. Displayed only for text messages. The default value depends on the global configuration. |
isSupportForward | MessageActionIDs.FORWARD | Forward. Displayed only for messages that were sent successfully. The default value is true. |
isSupportQuote | MessageActionIDs.QUOTE | Quote. Displayed only for messages that were sent successfully. The default value depends on the global configuration. |
isSupportMultiSelect | MessageActionIDs.MULTI_SELECT | Multi-select. Clicking it enters multi-select mode. The default value is true. |
isSupportDelete | MessageActionIDs.DELETE | Delete. Clicking it shows a confirmation dialog first. The default value depends on the global configuration. |
isSupportRecall | MessageActionIDs.RECALL | Recall. Displayed only for messages that you sent successfully and that were sent within the last 2 minutes. The default value depends on the global configuration. |
isSupportConvertToText | MessageActionIDs.CONVERT_TO_TEXT | Convert to text. Displayed only for voice messages that were sent successfully and do not currently show a transcription. The default value is true. |
isSupportTranslate | MessageActionIDs.TRANSLATE | Translate. Displayed only for text messages that were sent successfully and do not currently show a translation. The default value is true. |
isSupportListenFromHere | MessageActionIDs.LISTEN_FROM_HERE | Listen from here. Starting from this message, messages are read aloud or played in order through to the latest message. The default value is true. |
isSupportReaction | None | Reaction. When enabled, messages that were sent successfully show a "React" entry at the end of the long-press menu, and reactions are shown below the message. This entry is not an editable action item. The default value is true. |
customActions. MessageList adds the custom options you provide after the default action items (before the "React" entry):import io.trtc.tuikit.chat.uikit.components.messagelist.model.MessageCustomActionimport io.trtc.tuikit.chat.uikit.components.messagelist.ui.MessageListMessageList(conversationID = conversationID,customActions = listOf(MessageCustomAction(ID = "custom.message.share",title = "Share",iconResID = android.R.drawable.ic_menu_share,action = { message ->println("Share Message: ${message.msgID}")})))
MessageCustomAction are described below. Because action is not the last parameter, pass it as the named parameter action = { }:Field | Type | Description |
ID | String | Unique identifier of the action item. Required. |
title | String | Action item title. |
iconResID | Int | Drawable resource ID of the action item icon. No icon is shown when the value is 0. The icon is tinted with the theme text color. |
action | (MessageInfo) -> Unit | Callback invoked when the action item is clicked. The parameter is the message currently being long-pressed. |
dangerous | Boolean | Whether this is a dangerous action. The default is false. It currently does not affect the display style of the long-press menu. |
customizeActions of ChatMessageListConfig. Each time a message is long-pressed, the component first generates the default action items based on the switches and the message state, then runs the code in customizeActions, then appends the action items in customActions, and finally appends the "React" entry based on isSupportReaction.editorContext.message, the conversation ID from editorContext.conversationID, and the Android Context from editorContext.androidContext, and call the following methods to edit the action items:Method | Description |
add(item) | Adds an action item at the end. Throws an IllegalArgumentException if the item ID is blank or duplicates an existing action item. |
remove(ID) | Removes the action item with the specified ID. Returns false if it does not exist. |
replace(ID) { item -> ... } | Replaces the action item with the specified ID. Returns false if it does not exist. The replaced action item must keep the same ID; otherwise, an exception is thrown. |
insertBefore(anchorID, item) / insertAfter(anchorID, item) | Inserts an action item before or after the anchor action item. Returns false and does not insert if the anchor does not exist. The ID rules for item are the same as for add. |
moveBefore(ID, anchorID) / moveAfter(ID, anchorID) | Moves an action item to before or after the anchor action item. Returns false if either ID does not exist. |
clear() | Removes all action items. |
items | Read-only snapshot of the current action item list. |
import io.trtc.tuikit.atomicxcore.api.message.MessageTypeimport io.trtc.tuikit.chat.uikit.components.messagelist.config.ChatMessageListConfigimport io.trtc.tuikit.chat.uikit.components.messagelist.model.MessageActionIDsimport io.trtc.tuikit.chat.uikit.components.messagelist.model.MessageCustomActionval editActionsConfig = ChatMessageListConfig().customizeActions {// The message currently being long-pressedval message = editorContext.message// Remove "Select"remove(MessageActionIDs.MULTI_SELECT)// Change the title of "Delete". Keep the ID unchanged. Clicking it still shows a confirmation dialog firstreplace(MessageActionIDs.DELETE) { action ->action.copy(title = "Delete Message")}// Add "Favorite" for text messagesif (message.messageType == MessageType.TEXT) {val favoriteAction = MessageCustomAction(ID = "custom.message.favorite",title = "Favorite",iconResID = android.R.drawable.star_off,action = { target ->// Favorite the message target})// Place it after "Forward". If the current message has no "Forward" (for example, sending failed), append it to the endif (!insertAfter(MessageActionIDs.FORWARD, favoriteAction)) {add(favoriteAction)}}}MessageList(conversationID = conversationID, config = editActionsConfig)
Message List Actions (Default Options) | Message List Actions (Hide Copy Option) | Message List Actions (Add Share Option) |
![]() | ![]() | ![]() |
フィードバック