Skip to main content
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.
activity_chat.xml
<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)
    }
}
See the Conversation List + Message View guide for a complete layout.

Quick Start

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))
}
Prerequisites: CometChat SDK initialized with CometChatUIKit.init(), a user logged in, and the UI Kit dependency added. Or in a Fragment:
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
    return CometChatConversations(requireContext())
}

Filtering Conversations

Pass a ConversationsRequest.ConversationsRequestBuilder to control what loads:
conversations.setConversationsRequestBuilder(
    ConversationsRequest.ConversationsRequestBuilder()
        .setConversationType(CometChatConstants.CONVERSATION_TYPE_USER)
        .setLimit(20)
)

Filter Recipes

RecipeBuilder 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.
conversations.setOnItemClick { 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.
conversations.setOnItemLongClick { conversation ->
    // Show context menu
}

onBackPress

Fires when the user presses the back button in the toolbar.
conversations.setOnBackPress {
    finish()
}

onSearchClick

Fires when the user taps the search icon in the toolbar.
conversations.setOnSearchClick {
    // Open search screen
}

onSelection

Fires when conversations are selected/deselected in multi-select mode.
conversations.setSelectionMode(UIKitConstants.SelectionMode.MULTIPLE)
conversations.setOnSelection { selectedConversations ->
    updateToolbar(selectedConversations.size)
}

onError

Fires on internal errors (network failure, auth issue, SDK exception).
conversations.setOnError { exception ->
    Log.e("Conversations", "Error: ${exception.message}")
}

onLoad

Fires when the list is successfully fetched and loaded.
conversations.setOnLoad { conversations ->
    Log.d("Conversations", "Loaded ${conversations.size}")
}

onEmpty

Fires when the list is empty after loading.
conversations.setOnEmpty {
    Log.d("Conversations", "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 ListenerInternal 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 ParameterDescription
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.
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
    }
})

Title View

Replace the name / title text.
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 ?: ""
    }
})

Subtitle View

Replace the last message preview.
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"
            }
        }
    }
})

Trailing View

Replace the timestamp / badge / right section.
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"
    }
})

State Views

conversations.setEmptyView(customEmptyView)
conversations.setErrorView(customErrorView)
conversations.setLoadingView(customLoadingView)

Overflow Menu

conversations.setOverflowMenu(ImageButton(context).apply {
    setImageResource(R.drawable.ic_filter)
    setOnClickListener { /* show filter */ }
})

// 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 = { /* ... */ })
    )
}

Common Patterns

Minimal list — hide all chrome

conversations.setToolbarVisibility(View.GONE)
conversations.setSearchBoxVisibility(View.GONE)

Users-only conversations

conversations.setConversationsRequestBuilder(
    ConversationsRequest.ConversationsRequestBuilder()
        .setConversationType(CometChatConstants.CONVERSATION_TYPE_USER)
)

Custom date formatting

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"
})

Advanced Methods

Programmatic Selection

// 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()

ViewModel Access

val factory = CometChatConversationsViewModelFactory(
    repository = MyCustomRepository() // optional
)
val viewModel = ViewModelProvider(this, factory)
    .get(CometChatConversationsViewModel::class.java)
conversations.setViewModel(viewModel)
See ViewModel & Data for ListOperations, state observation, and custom repositories.

Style

Define a custom style in themes.xml:
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>

Style Properties

PropertyDescription
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