CometChatConversations renders a scrollable list of recent conversations with real-time updates for new messages, typing indicators, read receipts, and user presence.
Where It Fits
CometChatConversations is a list component. It renders recent conversations and emits the selected Conversation via onItemClick. Wire it to CometChatMessageHeader, CometChatMessageList, and CometChatMessageComposer to build a standard chat layout.
Kotlin (XML Views)
Jetpack Compose
< com.cometchat.uikit.kotlin.presentation.conversations.ui.CometChatConversations
android:id = "@+id/conversations"
android:layout_width = "match_parent"
android:layout_height = "match_parent" />
val conversations = findViewById < CometChatConversations >(R.id.conversations)
conversations. setOnItemClick { conversation ->
when ( val entity = conversation.conversationWith) {
is User -> navigateToUserChat (entity)
is Group -> navigateToGroupChat (entity)
}
}
CometChatConversations (
modifier = Modifier. fillMaxSize (),
onItemClick = { conversation ->
when ( val entity = conversation.conversationWith) {
is User -> navigateToUserChat (entity)
is Group -> navigateToGroupChat (entity)
}
}
)
See the Conversation List + Message View guide for a complete layout.
Quick Start
Kotlin (XML Views)
Jetpack Compose
Add to your layout XML: < com.cometchat.uikit.kotlin.presentation.conversations.ui.CometChatConversations
android:id = "@+id/conversations"
android:layout_width = "match_parent"
android:layout_height = "match_parent" />
Or programmatically: override fun onCreate (savedInstanceState: Bundle ?) {
super . onCreate (savedInstanceState)
setContentView ( CometChatConversations ( this ))
}
@Composable
fun ConversationsScreen () {
CometChatConversations (
modifier = Modifier. fillMaxSize ()
)
}
Prerequisites: CometChat SDK initialized with CometChatUIKit.init(), a user logged in, and the UI Kit dependency added.
Or in a Fragment:
Kotlin (XML Views)
Jetpack Compose
override fun onCreateView (inflater: LayoutInflater , container: ViewGroup ?, savedInstanceState: Bundle ?): View {
return CometChatConversations ( requireContext ())
}
override fun onCreateView (inflater: LayoutInflater , container: ViewGroup ?, savedInstanceState: Bundle ?): View {
return ComposeView ( requireContext ()). apply {
setContent { CometChatConversations () }
}
}
Filtering Conversations
Kotlin (XML Views)
Jetpack Compose
Pass a ConversationsRequest.ConversationsRequestBuilder to control what loads: conversations. setConversationsRequestBuilder (
ConversationsRequest. ConversationsRequestBuilder ()
. setConversationType (CometChatConstants.CONVERSATION_TYPE_USER)
. setLimit ( 20 )
)
CometChatConversations (
conversationsRequestBuilder = ConversationsRequest. ConversationsRequestBuilder ()
. setConversationType (CometChatConstants.CONVERSATION_TYPE_USER)
. setLimit ( 20 )
)
Filter Recipes
Recipe Builder method Only user conversations .setConversationType(CONVERSATION_TYPE_USER)Only group conversations .setConversationType(CONVERSATION_TYPE_GROUP)Limit per page .setLimit(10)With tags .setTags(listOf("vip")).withTags(true)Filter by user tags .withUserAndGroupTags(true).setUserTags(listOf("premium"))Filter by group tags .withUserAndGroupTags(true).setGroupTags(listOf("support"))
Pass the builder object, not the result of .build(). The component calls .build() internally. Default page size is 30 with infinite scroll.
Actions and Events
Callback Methods
onItemClick
Fires when a conversation row is tapped. Primary navigation hook.
Kotlin (XML Views)
Jetpack Compose
conversations. setOnItemClick { conversation ->
// Navigate to chat screen
}
CometChatConversations (
onItemClick = { conversation ->
// Navigate to chat screen
}
)
Replaces the default item-click behavior. Your custom lambda executes instead of the built-in navigation.
onItemLongClick
Fires when a conversation row is long-pressed. Use for additional actions like delete or select.
Kotlin (XML Views)
Jetpack Compose
conversations. setOnItemLongClick { conversation ->
// Show context menu
}
CometChatConversations (
onItemLongClick = { conversation ->
// Show context menu
}
)
onBackPress
Fires when the user presses the back button in the toolbar.
Kotlin (XML Views)
Jetpack Compose
conversations. setOnBackPress {
finish ()
}
CometChatConversations (
onBackPress = { /* navigate back */ }
)
onSearchClick
Fires when the user taps the search icon in the toolbar.
Kotlin (XML Views)
Jetpack Compose
conversations. setOnSearchClick {
// Open search screen
}
CometChatConversations (
onSearchClick = { /* open search */ }
)
onSelection
Fires when conversations are selected/deselected in multi-select mode.
Kotlin (XML Views)
Jetpack Compose
conversations. setSelectionMode (UIKitConstants.SelectionMode.MULTIPLE)
conversations. setOnSelection { selectedConversations ->
updateToolbar (selectedConversations.size)
}
CometChatConversations (
selectionMode = UIKitConstants.SelectionMode.MULTIPLE,
onSelection = { selectedConversations ->
updateToolbar (selectedConversations.size)
}
)
onError
Fires on internal errors (network failure, auth issue, SDK exception).
Kotlin (XML Views)
Jetpack Compose
conversations. setOnError { exception ->
Log. e ( "Conversations" , "Error: ${ exception.message } " )
}
CometChatConversations (
onError = { exception ->
Log. e ( "Conversations" , "Error: ${ exception.message } " )
}
)
onLoad
Fires when the list is successfully fetched and loaded.
Kotlin (XML Views)
Jetpack Compose
conversations. setOnLoad { conversations ->
Log. d ( "Conversations" , "Loaded ${ conversations.size } " )
}
CometChatConversations (
onLoad = { conversations ->
Log. d ( "Conversations" , "Loaded ${ conversations.size } " )
}
)
onEmpty
Fires when the list is empty after loading.
Kotlin (XML Views)
Jetpack Compose
conversations. setOnEmpty {
Log. d ( "Conversations" , "No conversations" )
}
CometChatConversations (
onEmpty = { /* no conversations */ }
)
Global Events
The component emits events via CometChatEvents that can be subscribed to from anywhere:
viewModelScope. launch {
CometChatEvents.conversationEvents. collect { event ->
when (event) {
is CometChatConversationEvent.ConversationDeleted -> { /* handle */ }
is CometChatConversationEvent.ConversationUpdated -> { /* handle */ }
}
}
}
SDK Events (Real-Time, Automatic)
The component listens to these SDK events internally. No manual setup needed.
SDK Listener Internal behavior onTextMessageReceived / onMediaMessageReceived / onCustomMessageReceivedMoves conversation to top, updates last message and unread count onTypingStarted / onTypingEndedShows/hides typing indicator in subtitle onMessagesDelivered / onMessagesReadUpdates receipt indicators onUserOnline / onUserOfflineUpdates presence status dot onGroupMemberJoined / onGroupMemberLeft / onGroupMemberKicked / onGroupMemberBannedUpdates group conversation metadata
Functionality
Method (Kotlin XML) Compose Parameter Description setBackIconVisibility(View.VISIBLE)hideBackIcon = falseToggle back button setToolbarVisibility(View.GONE)hideToolbar = trueToggle toolbar setSearchBoxVisibility(View.GONE)hideSearchBox = trueToggle search box setDisableSoundForMessages(true)disableSoundForMessages = trueDisable message sounds setCustomSoundForMessages(R.raw.sound)customSoundForMessages = R.raw.soundCustom message sound setSelectionMode(MULTIPLE)selectionMode = MULTIPLEEnable selection mode setTitle("Chats")title = "Chats"Custom toolbar title setSearchPlaceholderText("Search...")searchPlaceholderText = "Search..."Search placeholder
Custom View Slots
Leading View
Replace the avatar / left section.
Kotlin (XML Views)
Jetpack Compose
conversations. setLeadingView ( object : ConversationsViewHolderListener () {
override fun createView (context: Context , binding: CometchatConversationsListItemsBinding ): View {
return ImageView (context). apply {
layoutParams = ViewGroup. LayoutParams ( 48 .dp, 48 .dp)
}
}
override fun bindView (
context: Context , createdView: View , conversation: Conversation ,
typingIndicator: TypingIndicator ?, holder: RecyclerView .ViewHolder,
conversations: List < Conversation >, position: Int
) {
val imageView = createdView as ImageView
// Load avatar image
}
})
CometChatConversations (
leadingView = { conversation, typingIndicator ->
CometChatAvatar (
imageUrl = conversation.conversationWith?.avatar,
name = conversation.conversationWith?.name
)
}
)
Title View
Replace the name / title text.
Kotlin (XML Views)
Jetpack Compose
conversations. setTitleView ( object : ConversationsViewHolderListener () {
override fun createView (context: Context , binding: CometchatConversationsListItemsBinding ): View {
return TextView (context)
}
override fun bindView (
context: Context , createdView: View , conversation: Conversation ,
typingIndicator: TypingIndicator ?, holder: RecyclerView .ViewHolder,
conversations: List < Conversation >, position: Int
) {
(createdView as TextView).text = conversation.conversationWith?.name ?: ""
}
})
CometChatConversations (
titleView = { conversation, _ ->
Text (
text = conversation.conversationWith?.name ?: "" ,
style = CometChatTheme.typography.heading4Medium
)
}
)
Subtitle View
Replace the last message preview.
Kotlin (XML Views)
Jetpack Compose
conversations. setSubtitleView ( object : ConversationsViewHolderListener () {
override fun createView (context: Context , binding: CometchatConversationsListItemsBinding ): View {
return TextView (context). apply { maxLines = 1 ; ellipsize = TextUtils.TruncateAt.END }
}
override fun bindView (
context: Context , createdView: View , conversation: Conversation ,
typingIndicator: TypingIndicator ?, holder: RecyclerView .ViewHolder,
conversations: List < Conversation >, position: Int
) {
val textView = createdView as TextView
if (typingIndicator != null ) {
textView.text = "typing..."
} else {
textView.text = when ( val msg = conversation.lastMessage) {
is TextMessage -> msg.text
is MediaMessage -> "📎 ${ msg.attachment?.fileExtension ?: "Media" } "
else -> "New conversation"
}
}
}
})
CometChatConversations (
subtitleView = { conversation, typingIndicator ->
val text = if (typingIndicator != null ) "typing..." else {
when ( val msg = conversation.lastMessage) {
is TextMessage -> msg.text ?: ""
is MediaMessage -> "📎 ${ msg.attachment?.fileExtension ?: "Media" } "
else -> "New conversation"
}
}
Text (text, maxLines = 1 , overflow = TextOverflow.Ellipsis)
}
)
Trailing View
Replace the timestamp / badge / right section.
Kotlin (XML Views)
Jetpack Compose
conversations. setTrailingView ( object : ConversationsViewHolderListener () {
override fun createView (context: Context , binding: CometchatConversationsListItemsBinding ): View {
return TextView (context)
}
override fun bindView (
context: Context , createdView: View , conversation: Conversation ,
typingIndicator: TypingIndicator ?, holder: RecyclerView .ViewHolder,
conversations: List < Conversation >, position: Int
) {
(createdView as TextView).text = " ${ conversation.unreadMessageCount } unread"
}
})
CometChatConversations (
trailingView = { conversation, _ ->
if (conversation.unreadMessageCount > 0 ) {
Badge { Text ( " ${ conversation.unreadMessageCount } " ) }
}
}
)
State Views
Kotlin (XML Views)
Jetpack Compose
conversations. setEmptyView (customEmptyView)
conversations. setErrorView (customErrorView)
conversations. setLoadingView (customLoadingView)
CometChatConversations (
emptyView = { Text ( "No conversations yet" ) },
errorView = { onRetry -> Button (onClick = onRetry) { Text ( "Retry" ) } },
loadingView = { CircularProgressIndicator () }
)
Kotlin (XML Views)
Jetpack Compose
conversations. setOverflowMenu ( ImageButton (context). apply {
setImageResource (R.drawable.ic_filter)
setOnClickListener { /* show filter */ }
})
CometChatConversations (
overflowMenu = {
IconButton (onClick = { /* show filter */ }) {
Icon ( painterResource (R.drawable.ic_filter), "Filter" )
}
}
)
Kotlin (XML Views)
Jetpack Compose
// Replace all options
conversations. setOptions { context, conversation ->
listOf (
CometChatPopupMenu. MenuItem (id = "archive" , name = "Archive" , onClick = { /* ... */ }),
CometChatPopupMenu. MenuItem (id = "mute" , name = "Mute" , onClick = { /* ... */ })
)
}
// Append to defaults
conversations. setAddOptions { context, conversation ->
listOf (
CometChatPopupMenu. MenuItem (id = "pin" , name = "Pin" , onClick = { /* ... */ })
)
}
CometChatConversations (
options = { context, conversation ->
listOf (
MenuItem (id = "archive" , name = "Archive" , onClick = { /* ... */ }),
MenuItem (id = "mute" , name = "Mute" , onClick = { /* ... */ })
)
},
addOptions = { context, conversation ->
listOf ( MenuItem (id = "pin" , name = "Pin" , onClick = { /* ... */ }))
}
)
Common Patterns
Minimal list — hide all chrome
Kotlin (XML Views)
Jetpack Compose
conversations. setToolbarVisibility (View.GONE)
conversations. setSearchBoxVisibility (View.GONE)
CometChatConversations (
hideToolbar = true ,
hideSearchBox = true
)
Users-only conversations
Kotlin (XML Views)
Jetpack Compose
conversations. setConversationsRequestBuilder (
ConversationsRequest. ConversationsRequestBuilder ()
. setConversationType (CometChatConstants.CONVERSATION_TYPE_USER)
)
CometChatConversations (
conversationsRequestBuilder = ConversationsRequest. ConversationsRequestBuilder ()
. setConversationType (CometChatConstants.CONVERSATION_TYPE_USER)
)
Kotlin (XML Views)
Jetpack Compose
conversations. setDateTimeFormatter ( object : DateTimeFormatterCallback {
override fun today (timestamp: Long ) = "Today"
override fun yesterday (timestamp: Long ) = "Yesterday"
override fun otherDays (timestamp: Long ) = SimpleDateFormat ( "MMM d" , Locale. getDefault ()). format ( Date (timestamp))
override fun time (timestamp: Long ) = SimpleDateFormat ( "h:mm a" , Locale. getDefault ()). format ( Date (timestamp))
override fun minutes (diff: Long , timestamp: Long ) = " ${ diff } m ago"
override fun hours (diff: Long , timestamp: Long ) = " ${ diff } h ago"
override fun lastWeek (timestamp: Long ) = "Last week"
})
CometChatConversations (
dateTimeFormatter = object : DateTimeFormatterCallback {
override fun today (timestamp: Long ) = "Today"
override fun yesterday (timestamp: Long ) = "Yesterday"
override fun otherDays (timestamp: Long ) = SimpleDateFormat ( "MMM d" , Locale. getDefault ()). format ( Date (timestamp))
override fun time (timestamp: Long ) = SimpleDateFormat ( "h:mm a" , Locale. getDefault ()). format ( Date (timestamp))
override fun minutes (diff: Long , timestamp: Long ) = " ${ diff } m ago"
override fun hours (diff: Long , timestamp: Long ) = " ${ diff } h ago"
override fun lastWeek (timestamp: Long ) = "Last week"
}
)
Advanced Methods
Programmatic Selection
Kotlin (XML Views)
Jetpack Compose
// Enable selection
conversations. setSelectionMode (UIKitConstants.SelectionMode.MULTIPLE)
// Select a conversation
conversations. selectConversation (conversation, UIKitConstants.SelectionMode.MULTIPLE)
// Get selected
val selected = conversations. getSelectedConversations ()
// Clear
conversations. clearSelection ()
Selection is managed via the selectionMode and onSelection parameters. The component handles selection state internally.
ViewModel Access
val factory = CometChatConversationsViewModelFactory (
repository = MyCustomRepository () // optional
)
val viewModel = ViewModelProvider ( this , factory)
. get (CometChatConversationsViewModel:: class .java)
Kotlin (XML Views)
Jetpack Compose
conversations. setViewModel (viewModel)
CometChatConversations (
conversationsViewModel = viewModel
)
See ViewModel & Data for ListOperations, state observation, and custom repositories.
Style
Kotlin (XML Views)
Jetpack Compose
Define a custom style in themes.xml: < style name = "CustomConversationsStyle" parent = "CometChatConversationsStyle" >
< item name = "cometchatConversationsAvatarStyle" > @style/CustomAvatarStyle </ item >
< item name = "cometchatConversationsBadgeStyle" > @style/CustomBadgeStyle </ item >
< item name = "cometchatConversationsReceiptStyle" > @style/CustomReceiptStyle </ item >
< item name = "cometchatConversationsDateStyle" > @style/CustomDateStyle </ item >
</ style >
< style name = "AppTheme" parent = "CometChatTheme.DayNight" >
< item name = "cometchatConversationsStyle" > @style/CustomConversationsStyle </ item >
</ style >
CometChatConversations (
style = CometChatConversationsStyle. default (). copy (
backgroundColor = Color ( 0xFFF5F5F5 ),
titleTextColor = Color ( 0xFF141414 ),
searchBoxStyle = CometChatSearchBoxStyle. default (). copy (
backgroundColor = Color ( 0xFFFFFFFF )
),
itemStyle = CometChatConversationsItemStyle. default (). copy (
backgroundColor = Color.White,
titleTextColor = Color ( 0xFF141414 ),
subtitleTextColor = Color ( 0xFF727272 ),
avatarStyle = CometChatAvatarStyle. default (). copy (cornerRadius = 12 .dp),
badgeStyle = CometChatBadgeStyle. default (). copy (backgroundColor = Color ( 0xFFF76808 )),
dateStyle = CometChatDateStyle. default (). copy (textColor = Color ( 0xFFA1A1A1 )),
receiptStyle = CometChatReceiptStyle. default (). copy (),
statusIndicatorStyle = CometChatStatusIndicatorStyle. default (). copy (),
typingIndicatorStyle = CometChatTypingIndicatorStyle. default (). copy ()
)
)
)
Style Properties
Property Description backgroundColorList background color titleTextColorToolbar title color searchBoxStyleSearch box appearance itemStyle.backgroundColorRow background itemStyle.selectedBackgroundColorSelected row background itemStyle.titleTextColorConversation name color itemStyle.subtitleTextColorLast message preview color itemStyle.separatorColorRow separator color itemStyle.avatarStyleAvatar appearance itemStyle.badgeStyleUnread badge appearance itemStyle.dateStyleTimestamp appearance itemStyle.receiptStyleRead receipt icons itemStyle.statusIndicatorStyleOnline/offline indicator itemStyle.typingIndicatorStyleTyping indicator text itemStyle.mentionStyleMention highlight style
See Component Styling for the full reference.
Next Steps
Conversation List + Message View Build a full chat layout with this component
Component Styling Detailed styling reference with screenshots
ViewModel & Data Custom ViewModels, repositories, and ListOperations
View Slots Replace specific UI regions across all components