{"id":"d7a19152-cb6a-49a6-a215-3e8c1db9e669","shortId":"ceMYDY","kind":"skill","title":"cometchat-android-v6-compose-customization","tagline":"CometChat Android UIKit v6 Compose customization — BubbleFactory interface, slot lambdas, @Immutable style classes, and per-slot overrides","description":"> **Companion skills:** cometchat-android-v6-kotlin-customization (Views equivalent), cometchat-android-v6-compose-components, cometchat-android-v6-compose-theming, cometchat-android-v6-extensions (DataSource layer), cometchat-android-v6-events\n\n## Purpose\n\nCustomize CometChat Compose components — override message bubble rendering with BubbleFactory, use slot lambda parameters for per-slot overrides, and apply @Immutable style classes. This is the v6 replacement for v5's DataSource/ChatConfigurator pattern.\n\n## Use this skill when\n\n- Creating custom message bubble rendering for specific message types\n- Overriding individual bubble slots (avatar, header, footer, content, etc.)\n- Applying custom styles to components\n- Replacing the entire bubble layout for a message type\n\n## Do not use this skill when\n\n- Working with Kotlin Views customization (use `cometchat-android-v6-kotlin-customization`)\n- Changing theme colors globally (use `cometchat-android-v6-compose-theming`)\n- Extending the data layer (use `cometchat-android-v6-extensions`)\n\n## 1. BubbleFactory (Message Bubble Customization)\n\n`BubbleFactory` is an interface in `com.cometchat.uikit.compose.presentation.shared.messagebubble`. Each implementation handles a specific message type.\n\n### 1.1 Creating a Custom BubbleFactory\n\n```kotlin\nimport com.cometchat.uikit.compose.presentation.shared.messagebubble.BubbleFactory\nimport com.cometchat.chat.constants.CometChatConstants\nimport com.cometchat.chat.models.BaseMessage\nimport com.cometchat.uikit.core.constants.UIKitConstants.MessageBubbleAlignment\n\nclass LocationBubbleFactory : BubbleFactory {\n\n    override fun getCategory(): String = CometChatConstants.CATEGORY_CUSTOM\n    override fun getType(): String = \"location\"\n\n    override fun getContentView(\n        message: BaseMessage,\n        alignment: MessageBubbleAlignment,\n        style: CometChatMessageBubbleStyle,\n        textFormatters: List<CometChatTextFormatter>\n    ): @Composable () -> Unit = {\n        // Your custom content composable\n        val metadata = message.metadata\n        val lat = metadata?.optDouble(\"latitude\") ?: 0.0\n        val lng = metadata?.optDouble(\"longitude\") ?: 0.0\n        LocationMapView(latitude = lat, longitude = lng)\n    }\n}\n```\n\n### 1.2 Registering BubbleFactories\n\n```kotlin\nCometChatMessageList(\n    user = user,\n    bubbleFactories = listOf(\n        LocationBubbleFactory(),\n        PaymentBubbleFactory()\n    )\n)\n```\n\nThe list is converted to a map keyed by `\"category_type\"` internally via `toFactoryMap()`.\n\n### 1.3 Complete Bubble Replacement\n\nOverride `getBubbleView()` to replace the ENTIRE bubble (all slots):\n\n```kotlin\nclass CustomBubbleFactory : BubbleFactory {\n    override fun getCategory(): String = \"custom\"\n    override fun getType(): String = \"payment\"\n\n    override fun getBubbleView(\n        message: BaseMessage,\n        alignment: MessageBubbleAlignment\n    ): (@Composable () -> Unit)? = {\n        // Complete custom bubble — no header, footer, avatar, etc.\n        PaymentCard(message = message, alignment = alignment)\n    }\n}\n```\n\nWhen `getBubbleView()` returns non-null, all other slot methods are ignored.\n\n### 1.4 Slot Methods\n\nAll return `(@Composable () -> Unit)?` — return `null` to use defaults:\n\n| Method | Slot | Parameters |\n|---|---|---|\n| `getContentView()` | Main content | `message, alignment, style, textFormatters` |\n| `getLeadingView()` | Avatar | `message, alignment, style` |\n| `getHeaderView()` | Sender name | `message, alignment, style, showTime` |\n| `getReplyView()` | Reply preview | `message, alignment, style` |\n| `getBottomView()` | Moderation | `message, alignment, style, hideModerationView` |\n| `getStatusInfoView()` | Timestamp/receipts | `message, alignment, style, showTime` |\n| `getThreadView()` | Thread indicator | `message, alignment, style, onThreadRepliesClick` |\n| `getFooterView()` | Reactions | `message, alignment, style, onReactionClick, onReactionLongClick, onAddMoreReactionsClick` |\n\n### 1.5 Style Override\n\n```kotlin\noverride fun getBubbleStyle(\n    message: BaseMessage,\n    alignment: MessageBubbleAlignment\n): CometChatMessageBubbleStyle? {\n    return CometChatMessageBubbleStyle(\n        backgroundColor = Color(0xFFE8F5E9),\n        cornerRadius = 16.dp\n    )\n}\n```\n\nFactory style is the highest priority in the 3-tier style chain.\n\n### 1.6 Lifecycle\n\n```kotlin\noverride fun onDispose(message: BaseMessage) {\n    // Clean up resources when bubble leaves composition\n}\n```\n\n## 2. Slot Lambda Parameters (Per-Slot Overrides)\n\nOverride individual slots across ALL message types directly on `CometChatMessageList`:\n\n```kotlin\nCometChatMessageList(\n    user = user,\n\n    // Custom avatar for all messages\n    leadingView = { message, alignment ->\n        if (alignment == MessageAlignment.LEFT) {\n            AsyncImage(\n                model = message.sender?.avatar,\n                contentDescription = null,\n                modifier = Modifier.size(32.dp).clip(CircleShape)\n            )\n        }\n    },\n\n    // Custom timestamp for all messages\n    statusInfoView = { message, alignment ->\n        Text(\n            text = formatTime(message.sentAt),\n            style = CometChatTheme.typography.caption1Regular,\n            color = CometChatTheme.colorScheme.textColorTertiary\n        )\n    },\n\n    // Custom footer for all messages\n    footerView = { message, alignment ->\n        // Custom reactions display\n        ReactionsRow(message = message)\n    }\n)\n```\n\n### 2.1 All Slot Parameters\n\n| Parameter | Type | Slot |\n|---|---|---|\n| `leadingView` | `@Composable (BaseMessage, MessageAlignment) -> Unit` | Avatar |\n| `headerView` | `@Composable (BaseMessage, MessageAlignment) -> Unit` | Sender name |\n| `replyView` | `@Composable (BaseMessage, MessageAlignment) -> Unit` | Reply preview |\n| `contentView` | `@Composable (BaseMessage, MessageAlignment) -> Unit` | Main content |\n| `bottomView` | `@Composable (BaseMessage, MessageAlignment) -> Unit` | Moderation |\n| `statusInfoView` | `@Composable (BaseMessage, MessageAlignment) -> Unit` | Timestamp/receipts |\n| `threadView` | `@Composable (BaseMessage, MessageAlignment) -> Unit` | Thread indicator |\n| `footerView` | `@Composable (BaseMessage, MessageAlignment) -> Unit` | Reactions |\n\n### 2.2 Priority Order\n\nFor each slot, the resolution order is:\n1. Explicit slot lambda parameter (highest priority)\n2. BubbleFactory slot method\n3. Internal default rendering\n\n## 3. Style Classes\n\n### 3.1 Using Style Classes\n\n```kotlin\nCometChatConversations(\n    style = CometChatConversationsStyle.default(\n        backgroundColor = Color(0xFFF5F5F5),\n        titleTextColor = Color.Black,\n        titleTextStyle = CometChatTheme.typography.heading1Bold,\n        separatorColor = Color.LightGray,\n        itemStyle = CometChatConversationListItemStyle.default(\n            titleTextColor = Color.DarkGray\n        )\n    )\n)\n```\n\n### 3.2 Style Class Pattern\n\nAll style classes follow this pattern:\n\n```kotlin\n@Immutable\ndata class CometChatConversationsStyle(\n    val backgroundColor: Color,\n    val titleTextColor: Color,\n    val titleTextStyle: TextStyle,\n    // ... many properties\n) {\n    companion object {\n        @Composable\n        fun default(\n            backgroundColor: Color = CometChatTheme.colorScheme.backgroundColor1,\n            titleTextColor: Color = CometChatTheme.colorScheme.textColorPrimary,\n            // ... defaults from CometChatTheme\n        ): CometChatConversationsStyle = CometChatConversationsStyle(/* ... */)\n    }\n}\n```\n\nUse `Companion.default()` with named parameter overrides — never call the data class constructor directly.\n\n### 3.3 Nested Styles\n\nSome styles contain nested style classes:\n\n```kotlin\nCometChatConversationsStyle.default(\n    itemStyle = CometChatConversationListItemStyle.default(/* ... */),\n    popupMenuStyle = CometChatPopupMenuStyle.default(/* ... */),\n    emptyStateStyle = CometChatEmptyStateStyle.default(/* ... */),\n    errorStateStyle = CometChatErrorStateStyle.default(/* ... */),\n    loadingStateStyle = CometChatLoadingStateStyle.default(/* ... */),\n    dialogStyle = CometChatDialogStyle.default(/* ... */)\n)\n```\n\n## 4. Custom State Views\n\n```kotlin\nCometChatMessageList(\n    user = user,\n    loadingView = { CircularProgressIndicator() },\n    emptyView = { Text(\"No messages yet\") },\n    errorView = { Text(\"Something went wrong\") }\n)\n```\n\n## 5. v5 → v6 Migration\n\n| v5 Pattern | v6 Pattern |\n|---|---|\n| `ChatConfigurator.enable(decorator)` | `CometChatMessageList(bubbleFactories = listOf(...))` |\n| DataSource template methods | BubbleFactory slot methods |\n| Global decorator chain | Per-component factory registration |\n| No bubble replacement | `getBubbleView()` for complete replacement |\n| Theme-level styling only | Per-factory `getBubbleStyle()` |\n\n## Hard rules\n\n- BubbleFactory is an `interface` — implement it, don't extend an abstract class\n- `getCategory()` and `getType()` are REQUIRED — they identify which message type the factory handles\n- When `getBubbleView()` returns non-null, ALL other slot methods are ignored for that message type\n- Slot lambda parameters on `CometChatMessageList` override BubbleFactory slot methods — use lambdas for cross-type overrides, factories for per-type overrides\n- Style classes must use `Companion.default()` factory — the data class constructor requires ALL parameters\n- Do NOT confuse BubbleFactory (UI rendering) with DataSource (data fetching) — they are separate layers","tags":["cometchat","android","compose","customization","skills","agent-skills","ai-agent","chat","claude-code","cursor","messaging","nextjs"],"capabilities":["skill","source-cometchat","skill-cometchat-android-v6-compose-customization","topic-agent-skills","topic-ai-agent","topic-chat","topic-claude-code","topic-cometchat","topic-cursor","topic-messaging","topic-nextjs","topic-react","topic-react-native","topic-ui-kit"],"categories":["cometchat-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/cometchat/cometchat-skills/cometchat-android-v6-compose-customization","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add cometchat/cometchat-skills","source_repo":"https://github.com/cometchat/cometchat-skills","install_from":"skills.sh"}},"qualityScore":"0.463","qualityRationale":"deterministic score 0.46 from registry signals: · indexed on github topic:agent-skills · 27 github stars · SKILL.md body (9,443 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-18T19:04:46.074Z","embedding":null,"createdAt":"2026-05-07T13:05:05.154Z","updatedAt":"2026-05-18T19:04:46.074Z","lastSeenAt":"2026-05-18T19:04:46.074Z","tsv":"'0.0':240,246 '0xffe8f5e9':421 '0xfff5f5f5':623 '1':169,595 '1.1':187 '1.2':252 '1.3':277 '1.4':338 '1.5':405 '1.6':436 '16.dp':423 '2':451,602 '2.1':526 '2.2':585 '3':432,606,610 '3.1':613 '3.2':635 '3.3':691 '32.dp':492 '4':714 '5':734 'abstract':789 'across':462 'align':220,309,324,325,357,363,369,376,381,387,394,400,414,480,482,502,519 'android':3,8,29,37,43,49,56,144,155,166 'appli':80,116 'asyncimag':484 'avatar':111,319,361,474,487,538 'backgroundcolor':419,621,651,666 'backgroundcolor1':669 'basemessag':219,308,413,443,535,541,548,555,562,568,574,581 'bottomview':560 'bubbl':66,101,109,124,172,279,287,315,448,762 'bubblefactori':13,69,170,174,191,203,254,259,293,603,745,750,779,826,858 'call':685 'caption1regular':509 'categori':272 'chain':435,755 'chang':148 'chatconfigurator.enable':742 'circleshap':494 'circularprogressind':723 'class':19,83,201,291,612,616,637,641,648,688,699,790,843,850 'clean':444 'clip':493 'color':150,420,510,622,652,655,667,671 'color.black':625 'color.darkgray':634 'color.lightgray':630 'com.cometchat.chat.constants.cometchatconstants':196 'com.cometchat.chat.models.basemessage':198 'com.cometchat.uikit.compose.presentation.shared.messagebubble':179 'com.cometchat.uikit.compose.presentation.shared.messagebubble.bubblefactory':194 'com.cometchat.uikit.core.constants.uikitconstants.messagebubblealignment':200 'cometchat':2,7,28,36,42,48,55,61,143,154,165 'cometchat-android-v6-compose-components':35 'cometchat-android-v6-compose-customization':1 'cometchat-android-v6-compose-theming':41,153 'cometchat-android-v6-events':54 'cometchat-android-v6-extensions':47,164 'cometchat-android-v6-kotlin-customization':27,142 'cometchatconstants.category':208 'cometchatconvers':618 'cometchatconversationlistitemstyle.default':632,703 'cometchatconversationsstyl':649,676,677 'cometchatconversationsstyle.default':620,701 'cometchatdialogstyle.default':713 'cometchatemptystatestyle.default':707 'cometchaterrorstatestyle.default':709 'cometchatloadingstatestyle.default':711 'cometchatmessagebubblestyl':223,416,418 'cometchatmessagelist':256,468,470,719,744,824 'cometchatpopupmenustyle.default':705 'cometchatthem':675 'cometchattheme.colorscheme':668 'cometchattheme.colorscheme.textcolorprimary':672 'cometchattheme.colorscheme.textcolortertiary':511 'cometchattheme.typography':508,627 'companion':25,661 'companion.default':679,846 'complet':278,313,766 'compon':40,63,120,758 'compos':5,11,39,45,62,157,226,231,311,343,534,540,547,554,561,567,573,580,663 'composit':450 'confus':857 'constructor':689,851 'contain':696 'content':114,230,355,559 'contentdescript':488 'contentview':553 'convert':266 'cornerradius':422 'creat':98,188 'cross':833 'cross-typ':832 'custom':6,12,32,60,99,117,140,147,173,190,209,229,298,314,473,495,512,520,715 'custombubblefactori':292 'data':161,647,687,849,863 'datasourc':52,747,862 'datasource/chatconfigurator':92 'decor':743,754 'default':349,608,665,673 'dialogstyl':712 'direct':466,690 'display':522 'emptystatestyl':706 'emptyview':724 'entir':123,286 'equival':34 'errorstatestyl':708 'errorview':729 'etc':115,320 'event':58 'explicit':596 'extend':159,787 'extens':51,168 'factori':424,759,775,802,836,847 'fetch':864 'follow':642 'footer':113,318,513 'footerview':517,579 'formattim':505 'fun':205,211,216,295,300,305,410,440,664 'getbottomview':378 'getbubblestyl':411,776 'getbubbleview':282,306,327,764,805 'getcategori':206,296,791 'getcontentview':217,353 'getfooterview':397 'getheaderview':365 'getleadingview':360 'getreplyview':372 'getstatusinfoview':384 'getthreadview':390 'gettyp':212,301,793 'global':151,753 'handl':182,803 'hard':777 'header':112,317 'headerview':539 'heading1bold':628 'hidemoderationview':383 'highest':428,600 'identifi':797 'ignor':337,815 'immut':17,81,646 'implement':181,783 'import':193,195,197,199 'indic':392,578 'individu':108,460 'interfac':14,177,782 'intern':274,607 'itemstyl':631,702 'key':270 'kotlin':31,138,146,192,255,290,408,438,469,617,645,700,718 'lambda':16,72,453,598,821,830 'lat':236,249 'latitud':239,248 'layer':53,162,868 'layout':125 'leadingview':478,533 'leav':449 'level':770 'lifecycl':437 'list':225,264 'listof':260,746 'lng':242,251 'loadingstatestyl':710 'loadingview':722 'locat':214 'locationbubblefactori':202,261 'locationmapview':247 'longitud':245,250 'main':354,558 'mani':659 'map':269 'messag':65,100,105,128,171,185,218,307,322,323,356,362,368,375,380,386,393,399,412,442,464,477,479,499,501,516,518,524,525,727,799,818 'message.metadata':234 'message.sender':486 'message.sentat':506 'messagealign':536,542,549,556,563,569,575,582 'messagealignment.left':483 'messagebubblealign':221,310,415 'metadata':233,237,243 'method':335,340,350,605,749,752,813,828 'migrat':737 'model':485 'moder':379,565 'modifi':490 'modifier.size':491 'must':844 'name':367,545,681 'nest':692,697 'never':684 'non':330,808 'non-nul':329,807 'null':331,346,489,809 'object':662 'onaddmorereactionsclick':404 'ondispos':441 'onreactionclick':402 'onreactionlongclick':403 'onthreadrepliesclick':396 'optdoubl':238,244 'order':587,593 'overrid':24,64,78,107,204,210,215,281,294,299,304,407,409,439,458,459,683,825,835,841 'paramet':73,352,454,529,530,599,682,822,854 'pattern':93,638,644,739,741 'payment':303 'paymentbubblefactori':262 'paymentcard':321 'per':22,76,456,757,774,839 'per-compon':756 'per-factori':773 'per-slot':21,75,455 'per-typ':838 'popupmenustyl':704 'preview':374,552 'prioriti':429,586,601 'properti':660 'purpos':59 'reaction':398,521,584 'reactionsrow':523 'regist':253 'registr':760 'render':67,102,609,860 'replac':88,121,280,284,763,767 'repli':373,551 'replyview':546 'requir':795,852 'resolut':592 'resourc':446 'return':328,342,345,417,806 'rule':778 'sender':366,544 'separ':867 'separatorcolor':629 'showtim':371,389 'skill':26,96,134 'skill-cometchat-android-v6-compose-customization' 'slot':15,23,71,77,110,289,334,339,351,452,457,461,528,532,590,597,604,751,812,820,827 'someth':731 'source-cometchat' 'specif':104,184 'state':716 'statusinfoview':500,566 'string':207,213,297,302 'style':18,82,118,222,358,364,370,377,382,388,395,401,406,425,434,507,611,615,619,636,640,693,695,698,771,842 'templat':748 'text':503,504,725,730 'textformatt':224,359 'textstyl':658 'theme':46,149,158,769 'theme-level':768 'thread':391,577 'threadview':572 'tier':433 'timestamp':496 'timestamp/receipts':385,571 'titletextcolor':624,633,654,670 'titletextstyl':626,657 'tofactorymap':276 'topic-agent-skills' 'topic-ai-agent' 'topic-chat' 'topic-claude-code' 'topic-cometchat' 'topic-cursor' 'topic-messaging' 'topic-nextjs' 'topic-react' 'topic-react-native' 'topic-ui-kit' 'type':106,129,186,273,465,531,800,819,834,840 'ui':859 'uikit':9 'unit':227,312,344,537,543,550,557,564,570,576,583 'use':70,94,132,141,152,163,348,614,678,829,845 'user':257,258,471,472,720,721 'v5':90,735,738 'v6':4,10,30,38,44,50,57,87,145,156,167,736,740 'val':232,235,241,650,653,656 'via':275 'view':33,139,717 'went':732 'work':136 'wrong':733 'yet':728","prices":[{"id":"7d07ddf8-097a-4ec7-b7d9-c222dd64df68","listingId":"d7a19152-cb6a-49a6-a215-3e8c1db9e669","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"cometchat","category":"cometchat-skills","install_from":"skills.sh"},"createdAt":"2026-05-07T13:05:05.154Z"}],"sources":[{"listingId":"d7a19152-cb6a-49a6-a215-3e8c1db9e669","source":"github","sourceId":"cometchat/cometchat-skills/cometchat-android-v6-compose-customization","sourceUrl":"https://github.com/cometchat/cometchat-skills/tree/main/skills/cometchat-android-v6-compose-customization","isPrimary":false,"firstSeenAt":"2026-05-07T13:05:05.154Z","lastSeenAt":"2026-05-18T19:04:46.074Z"}],"details":{"listingId":"d7a19152-cb6a-49a6-a215-3e8c1db9e669","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"cometchat","slug":"cometchat-android-v6-compose-customization","github":{"repo":"cometchat/cometchat-skills","stars":27,"topics":["agent-skills","ai-agent","chat","claude-code","cometchat","cursor","messaging","nextjs","react","react-native","ui-kit"],"license":null,"html_url":"https://github.com/cometchat/cometchat-skills","pushed_at":"2026-05-18T05:04:24Z","description":"Add CometChat chat to any React, Next.js, React Native, Angular, Android, iOS, or Flutter project through your AI coding agent. Works with Claude Code, Cursor, Codex, VS Code Copilot, Windsurf, Cline, Kiro, and 50+ more agents.","skill_md_sha":"0fbaa802eaf14742fa2ba9895a8b8364d029101f","skill_md_path":"skills/cometchat-android-v6-compose-customization/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/cometchat/cometchat-skills/tree/main/skills/cometchat-android-v6-compose-customization"},"layout":"multi","source":"github","category":"cometchat-skills","frontmatter":{"name":"cometchat-android-v6-compose-customization","license":"MIT","description":"CometChat Android UIKit v6 Compose customization — BubbleFactory interface, slot lambdas, @Immutable style classes, and per-slot overrides","compatibility":"Android 9.0+ (API 28); Kotlin 1.9+; com.cometchat:chatuikit-compose-android:6.x"},"skills_sh_url":"https://skills.sh/cometchat/cometchat-skills/cometchat-android-v6-compose-customization"},"updatedAt":"2026-05-18T19:04:46.074Z"}}