{"id":"7aa20b95-d165-45e1-8572-a8dffdecb4d8","shortId":"sje9W7","kind":"skill","title":"cometchat-flutter-v5-core","tagline":"Use when writing any code that uses CometChat Flutter UIKit v5 (cometchat_chat_uikit v5.2.14, cometchat_calls_uikit v5.0.15, cometchat_uikit_shared v5.2.3). Contains hard rules that prevent silent failures.","description":"# CometChat Flutter UIKit v5 — Core Rules\n\nNon-negotiable constraints for all CometChat UIKit v5 code. Violating these causes silent failures or crashes.\n\n## Key v5 Architecture Facts\n\n- State management: **GetX** (GetBuilder, GetxController, Get.put, Get.find, Get.delete)\n- Separate packages: `cometchat_chat_uikit` + `cometchat_calls_uikit` + `cometchat_uikit_shared`\n- SDK: `cometchat_sdk ^4.1.2` + `cometchat_calls_sdk ^4.2.2`\n- **Imports — two barrels.** For chat-only projects: `package:cometchat_chat_uikit/cometchat_chat_uikit.dart`. For projects that also need voice/video calling: ADD `package:cometchat_calls_uikit/cometchat_calls_uikit.dart` as a SECOND import — the calls barrel re-exports shared + SDK only and does NOT re-export `cometchat_chat_uikit`. Chat widgets like `CometChatConversations`, `CometChatMessageList`, `CometChatMessageComposer` are reachable only through the chat barrel.\n- `CometChatUIKit.login(uid)` takes a **String** directly (not an object)\n- No ServiceLocator pattern — controllers are created via `Get.put()` internally\n- Style classes use `ThemeExtension` with `merge()` pattern\n\n## Rule: INIT_FIRST\n\n`CometChatUIKit.init()` must complete before any login, component usage, or SDK call.\n\n```dart\n// ✅ CORRECT\nfinal settings = (UIKitSettingsBuilder()\n      ..appId = 'APP_ID'\n      ..region = 'us'\n      ..authKey = 'AUTH_KEY'\n      ..subscriptionType = CometChatSubscriptionType.allUsers)\n    .build();\n\nCometChatUIKit.init(\n  uiKitSettings: settings,\n  onSuccess: (_) => debugPrint('Init done'),\n  onError: (e) => debugPrint('Init failed: ${e.message}'),\n);\n\n// ❌ WRONG — login before init completes\nCometChatUIKit.init(uiKitSettings: settings);\nCometChatUIKit.login('uid'); // Race condition\n```\n\n## Rule: AUTH_CHECK_AFTER_INIT\n\nAfter `CometChatUIKit.init()` completes, the static field `CometChatUIKit.loggedInUser` is populated if a cached session exists (init internally calls `getLoggedInUser()`). You can check it synchronously in `onSuccess`, or use `CometChatUIKit.getLoggedInUser()` for an explicit async check.\n\n```dart\n// ✅ CORRECT — synchronous check after init\nCometChatUIKit.init(\n  uiKitSettings: settings,\n  onSuccess: (_) {\n    final hasUser = CometChatUIKit.loggedInUser != null;\n    // Route to home or login\n  },\n);\n\n// ✅ ALSO CORRECT — explicit async check (used by master app)\nCometChatUIKit.init(\n  uiKitSettings: settings,\n  onSuccess: (_) async {\n    final user = await CometChatUIKit.getLoggedInUser();\n    if (user != null) {\n      await CometChatUIKit.login(user.uid, onSuccess: ...);\n    }\n  },\n);\n```\n\nNote: `CometChatUIKit.login()` handles re-login gracefully — if the user is already logged in with the same UID, it returns the cached user without hitting the server.\n\n## Rule: LISTENER_LIFECYCLE\n\nSDK listeners MUST be registered with a unique ID in `initState()` (or GetxController `onInit()`) and removed in `dispose()` (or `onClose()`).\n\n```dart\n// ✅ CORRECT\nclass _MyScreenState extends State<MyScreen> with MessageListener {\n  late final String _listenerId;\n\n  @override\n  void initState() {\n    super.initState();\n    _listenerId = 'my_screen_${DateTime.now().millisecondsSinceEpoch}';\n    CometChat.addMessageListener(_listenerId, this);\n  }\n\n  @override\n  void dispose() {\n    CometChat.removeMessageListener(_listenerId);\n    super.dispose();\n  }\n}\n\n// ❌ WRONG — hardcoded ID causes collisions; missing dispose removal\nCometChat.addMessageListener('messages', this); // Collision!\n```\n\n## Rule: THEME_CACHE\n\nCache theme values in `didChangeDependencies()` — unconditionally, no flag needed. Never call `CometChatThemeHelper.getColorPalette(context)` in `build()`.\n\n`getColorPalette()` creates a new `CometChatColorPalette` object every call, resolving each token individually via `Theme.of(context)`. During keyboard animation, `MediaQuery` changes trigger rebuilds, making this expensive in `build()`.\n\n```dart\n// ✅ CORRECT — matches actual package pattern (no flag)\n@override\nvoid didChangeDependencies() {\n  super.didChangeDependencies();\n  colorPalette = CometChatThemeHelper.getColorPalette(context);\n  spacing = CometChatThemeHelper.getSpacing(context);\n  typography = CometChatThemeHelper.getTypography(context);\n}\n\n// ❌ WRONG — lookup in build causes jank\n@override\nWidget build(BuildContext context) {\n  final colors = CometChatThemeHelper.getColorPalette(context); // Expensive!\n  return Container(color: colors.primary);\n}\n```\n\nDo NOT use a `_themeInitialized` flag — it prevents theme updates when the system switches between light/dark mode.\n\n## Rule: SUBSCRIPTION_TYPE_REQUIRED\n\nOmitting `subscriptionType` in `UIKitSettingsBuilder` silently disables all presence events (online/offline, typing indicators). No error is thrown.\n\n```dart\n// ✅ CORRECT\nUIKitSettingsBuilder()\n  ..subscriptionType = CometChatSubscriptionType.allUsers\n```\n\n## Rule: REGION_LOWERCASE\n\nRegion must be a lowercase string: `'us'`, `'eu'`, or `'in'`.\n\n## Rule: MUID_PRESERVATION\n\nWhen handling `ccMessageSent` events, compare by `muid` first, then `id` — the SDK may return an empty `muid` in the success callback.\n\n## Pattern: Callback → Async Bridge (Completer)\n\n```dart\nimport 'dart:async';\n\nFuture<bool> initAsync(UIKitSettings settings) {\n  final completer = Completer<bool>();\n  CometChatUIKit.init(\n    uiKitSettings: settings,\n    onSuccess: (_) => completer.complete(true),\n    onError: (e) => completer.complete(false),\n  );\n  return completer.future;\n}\n```\n\n## v5 Component Architecture Pattern (GetX)\n\n```\n{component}/\n├── cometchat_{component}.dart              # StatefulWidget\n├── cometchat_{component}_controller.dart   # extends GetxController\n├── cometchat_{component}_style.dart        # ThemeExtension with merge()\n└── {component}_builder_protocol.dart       # Request builder protocol\n```\n\nInternal lifecycle:\n```dart\n@override\nvoid initState() {\n  super.initState();\n  tag = widget.controllerTag ?? 'default_tag_${DateTime.now().millisecondsSinceEpoch}';\n  controller = Get.put<Controller>(Controller(...), tag: tag);\n}\n\n@override\nvoid dispose() {\n  if (widget.controllerTag == null) {\n    Get.delete<Controller>(tag: tag);\n  }\n  super.dispose();\n}\n```\n\n## Android Build Requirements\n\n- `android.useAndroidX=true` and `android.enableJetifier=true` in `gradle.properties`\n- `minSdk 26` in `android/app/build.gradle`\n- ProGuard: `-keep class com.cometchat.** { *; }` and `-keep interface com.cometchat.** { *; }`\n\n## Top 10 Error Debugging\n\n| Symptom | Cause | Fix |\n|---------|-------|-----|\n| \"Authentication null\" | `CometChatUIKit.init()` not called | Call init before login/components |\n| \"APP ID null\" | appId not set in UIKitSettingsBuilder | Set `..appId = 'YOUR_APP_ID'` |\n| No typing indicators / presence | `subscriptionType` not set | Set `..subscriptionType = CometChatSubscriptionType.allUsers` |\n| Theme jank during keyboard | Theme looked up in `build()` | Cache in `didChangeDependencies()` |\n| Listener leak / duplicate events | Listener not removed in `dispose()` | Always remove with same ID |\n| GetX controller not found | Using `Get.find()` before `Get.put()` | Let UIKit components manage their own controllers |\n| Region error | Uppercase region string | Use lowercase: 'us', 'eu', 'in' |\n| Release build crash | Missing ProGuard keep rules | Add `-keep class com.cometchat.** { *; }` |\n\n## Checklist — Every CometChat v5 Screen\n\n- [ ] `CometChatUIKit.init()` called before any usage\n- [ ] `subscriptionType` set in UIKitSettingsBuilder\n- [ ] `region` is lowercase\n- [ ] Theme cached in `didChangeDependencies()`, not `build()`\n- [ ] SDK listeners registered with unique ID, removed in `dispose()`\n- [ ] Colors from `CometChatThemeHelper`, never hardcoded\n- [ ] Imports: `package:cometchat_chat_uikit/cometchat_chat_uikit.dart` always; ADD `package:cometchat_calls_uikit/cometchat_calls_uikit.dart` if you use voice/video","tags":["cometchat","flutter","core","skills","agent-skills","ai-agent","chat","claude-code","cursor","messaging","nextjs","react"],"capabilities":["skill","source-cometchat","skill-cometchat-flutter-v5-core","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-flutter-v5-core","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 (7,840 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:49.916Z","embedding":null,"createdAt":"2026-05-07T13:05:09.191Z","updatedAt":"2026-05-18T19:04:49.916Z","lastSeenAt":"2026-05-18T19:04:49.916Z","tsv":"'10':673 '26':661 '4.1.2':85 '4.2.2':89 'actual':451 'add':109,769,816 'alreadi':322 'also':105,286 'alway':732,815 'android':650 'android.enablejetifier':656 'android.useandroidx':653 'android/app/build.gradle':663 'anim':438 'app':194,294,688,699 'appid':193,691,697 'architectur':61,598 'async':265,289,299,570,576 'auth':199,230 'authent':679 'authkey':198 'await':302,307 'barrel':92,120,148 'bridg':571 'build':203,420,447,472,477,651,719,763,795 'buildcontext':478 'builder':620 'builder_protocol.dart':618 'cach':245,332,405,406,720,791 'call':22,77,87,108,112,119,187,250,416,428,683,684,779,819 'callback':567,569 'caus':54,394,473,677 'ccmessages':549 'chang':440 'chat':18,74,95,100,134,136,147,813 'chat-on':94 'check':231,254,266,270,290 'checklist':773 'class':168,363,666,771 'code':10,51 'collis':395,402 'color':481,487,805 'colorpalett':460 'colors.primary':488 'com.cometchat':667,671,772 'cometchat':2,13,17,21,25,36,48,73,76,79,83,86,99,111,133,602,606,611,775,812,818 'cometchat-flutter-v5-core':1 'cometchat.addmessagelistener':382,399 'cometchat.removemessagelistener':388 'cometchatcolorpalett':425 'cometchatconvers':139 'cometchatmessagecompos':141 'cometchatmessagelist':140 'cometchatsubscriptiontype.allusers':202,530,710 'cometchatthemehelp':807 'cometchatthemehelper.getcolorpalette':417,461,482 'cometchatthemehelper.getspacing':464 'cometchatthemehelper.gettypography':467 'cometchatuikit.getloggedinuser':261,303 'cometchatuikit.init':177,204,222,235,273,295,584,681,778 'cometchatuikit.loggedinuser':240,279 'cometchatuikit.login':149,225,308,312 'compar':551 'complet':179,221,236,572,582,583 'completer.complete':588,592 'completer.future':595 'compon':183,597,601,603,607,612,617,747 'condit':228 'constraint':45 'contain':29,486 'context':418,435,462,465,468,479,483 'control':161,635,637,738,751 'controller.dart':608 'core':5,40 'correct':189,268,287,362,449,527 'crash':58,764 'creat':163,422 'dart':188,267,361,448,526,573,575,604,624 'datetime.now':380,633 'debug':675 'debugprint':208,213 'default':631 'didchangedepend':410,458,722,793 'direct':154 'disabl':515 'dispos':358,387,397,642,731,804 'done':210 'duplic':725 'e':212,591 'e.message':216 'empti':562 'error':523,674,753 'eu':541,760 'event':518,550,726 'everi':427,774 'exist':247 'expens':445,484 'explicit':264,288 'export':123,132 'extend':365,609 'fact':62 'fail':215 'failur':35,56 'fals':593 'field':239 'final':190,277,300,370,480,581 'first':176,554 'fix':678 'flag':413,455,494 'flutter':3,14,37 'found':740 'futur':577 'get.delete':70,646 'get.find':69,742 'get.put':68,165,636,744 'getbuild':66 'getcolorpalett':421 'getloggedinus':251 'getx':65,600,737 'getxcontrol':67,353,610 'grace':317 'gradle.properties':659 'handl':313,548 'hard':30 'hardcod':392,809 'hasus':278 'hit':335 'home':283 'id':195,349,393,556,689,700,736,801 'import':90,117,574,810 'indic':521,703 'individu':432 'init':175,209,214,220,233,248,272,685 'initasync':578 'initst':351,375,627 'interfac':670 'intern':166,249,622 'jank':474,712 'keep':665,669,767,770 'key':59,200 'keyboard':437,714 'late':369 'leak':724 'let':745 'lifecycl':340,623 'light/dark':504 'like':138 'listen':339,342,723,727,797 'listenerid':372,377,383,389 'log':323 'login':182,218,285,316 'login/components':687 'look':716 'lookup':470 'lowercas':533,538,758,789 'make':443 'manag':64,748 'master':293 'match':450 'may':559 'mediaqueri':439 'merg':172,616 'messag':400 'messagelisten':368 'millisecondssinceepoch':381,634 'minsdk':660 'miss':396,765 'mode':505 'muid':545,553,563 'must':178,343,535 'myscreenst':364 'need':106,414 'negoti':44 'never':415,808 'new':424 'non':43 'non-negoti':42 'note':311 'null':280,306,645,680,690 'object':157,426 'omit':510 'onclos':360 'onerror':211,590 'oninit':354 'online/offline':519 'onsuccess':207,258,276,298,310,587 'overrid':373,385,456,475,625,640 'packag':72,98,110,452,811,817 'pattern':160,173,453,568,599 'popul':242 'presenc':517,704 'preserv':546 'prevent':33,496 'proguard':664,766 'project':97,103 'protocol':621 'race':227 're':122,131,315 're-export':121,130 're-login':314 'reachabl':143 'rebuild':442 'region':196,532,534,752,755,787 'regist':345,798 'releas':762 'remov':356,398,729,733,802 'request':619 'requir':509,652 'resolv':429 'return':330,485,560,594 'rout':281 'rule':31,41,174,229,338,403,506,531,544,768 'screen':379,777 'sdk':82,84,88,125,186,341,558,796 'second':116 'separ':71 'server':337 'serviceloc':159 'session':246 'set':191,206,224,275,297,580,586,693,696,707,708,784 'share':27,81,124 'silent':34,55,514 'skill' 'skill-cometchat-flutter-v5-core' 'source-cometchat' 'space':463 'state':63,366 'statefulwidget':605 'static':238 'string':153,371,539,756 'style':167 'style.dart':613 'subscript':507 'subscriptiontyp':201,511,529,705,709,783 'success':566 'super.didchangedependencies':459 'super.dispose':390,649 'super.initstate':376,628 'switch':502 'symptom':676 'synchron':256,269 'system':501 'tag':629,632,638,639,647,648 'take':151 'theme':404,407,497,711,715,790 'theme.of':434 'themeextens':170,614 'themeiniti':493 'thrown':525 'token':431 'top':672 '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' 'trigger':441 'true':589,654,657 'two':91 'type':508,520,702 'typographi':466 'uid':150,226,328 'uikit':15,19,23,26,38,49,75,78,80,135,746 'uikit/cometchat_calls_uikit.dart':113,820 'uikit/cometchat_chat_uikit.dart':101,814 'uikitset':205,223,274,296,579,585 'uikitsettingsbuild':192,513,528,695,786 'uncondit':411 'uniqu':348,800 'updat':498 'uppercas':754 'us':197,540,759 'usag':184,782 'use':6,12,169,260,291,491,741,757,823 'user':301,305,320,333 'user.uid':309 'v5':4,16,39,50,60,596,776 'v5.0.15':24 'v5.2.14':20 'v5.2.3':28 'valu':408 'via':164,433 'violat':52 'voice/video':107,824 'void':374,386,457,626,641 'widget':137,476 'widget.controllertag':630,644 'without':334 'write':8 'wrong':217,391,469","prices":[{"id":"82bf37c2-890e-45a9-84e8-dbae1bf2cd0d","listingId":"7aa20b95-d165-45e1-8572-a8dffdecb4d8","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:09.191Z"}],"sources":[{"listingId":"7aa20b95-d165-45e1-8572-a8dffdecb4d8","source":"github","sourceId":"cometchat/cometchat-skills/cometchat-flutter-v5-core","sourceUrl":"https://github.com/cometchat/cometchat-skills/tree/main/skills/cometchat-flutter-v5-core","isPrimary":false,"firstSeenAt":"2026-05-07T13:05:09.191Z","lastSeenAt":"2026-05-18T19:04:49.916Z"}],"details":{"listingId":"7aa20b95-d165-45e1-8572-a8dffdecb4d8","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"cometchat","slug":"cometchat-flutter-v5-core","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":"53bd99878a7c4e73afbe0b9536fdcdf437971290","skill_md_path":"skills/cometchat-flutter-v5-core/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/cometchat/cometchat-skills/tree/main/skills/cometchat-flutter-v5-core"},"layout":"multi","source":"github","category":"cometchat-skills","frontmatter":{"name":"cometchat-flutter-v5-core","license":"MIT","description":"Use when writing any code that uses CometChat Flutter UIKit v5 (cometchat_chat_uikit v5.2.14, cometchat_calls_uikit v5.0.15, cometchat_uikit_shared v5.2.3). Contains hard rules that prevent silent failures.","compatibility":"cometchat_chat_uikit ^5.2.14; cometchat_calls_uikit ^5.0.15; cometchat_uikit_shared ^5.2.3; cometchat_sdk ^4.1.2; get ^4.6.5"},"skills_sh_url":"https://skills.sh/cometchat/cometchat-skills/cometchat-flutter-v5-core"},"updatedAt":"2026-05-18T19:04:49.916Z"}}