{"id":"992cddc5-bcd4-49cc-99d7-10ebd559db83","shortId":"BKFUBk","kind":"skill","title":"cometchat-flutter-v6-core","tagline":"Use when writing any code that uses cometchat_chat_uikit. Contains hard rules that prevent silent failures, crashes, and subtle bugs. Covers CometChatUIKit.init, login, logout, UIKitSettings, UIKitSettingsBuilder, listener lifecycle, theme caching, Scaffold resizeToAvoidBottomIns","description":"# CometChat Flutter UIKit — Core Rules\n\nNon-negotiable constraints for all CometChat UIKit code. Violating these causes silent failures or crashes.\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\nawait CometChatUIKit.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 (in its `onSuccess`), the static field `CometChatUIKit.loggedInUser` is already populated if a cached session exists. Use this synchronous check — do NOT call `CometChat.getLoggedInUser()` separately.\n\n```dart\n// ✅ CORRECT — synchronous check after init completes\nCometChatUIKit.init(\n  uiKitSettings: settings,\n  onSuccess: (_) {\n    final hasUser = CometChatUIKit.loggedInUser != null;\n    // Route to home or login based on hasUser\n  },\n);\n\n// ❌ WRONG — separate async getLoggedInUser call after init\nCometChatUIKit.init(\n  uiKitSettings: settings,\n  onSuccess: (_) {\n    CometChat.getLoggedInUser(\n      onSuccess: (user) { ... },  // Unreliable when no session exists\n      onError: (e) { ... },\n    );\n  },\n);\n```\n\nThe `init()` method internally calls `getLoggedInUser()` and sets `CometChatUIKit.loggedInUser` before firing `onSuccess`. Calling it again is redundant and the callback-based version can silently fail when no session exists (the SDK logs \"Please log in to CometChat before calling this method\" and neither callback fires consistently).\n\nThis also applies to `login()` and `loginWithAuthToken()` — all three populate `CometChatUIKit.loggedInUser` before calling `onSuccess`.\n\n```dart\n// ❌ ALSO WRONG — async getLoggedInUser after init (redundant native bridge round-trip)\nCometChatUIKit.init(\n  uiKitSettings: settings,\n  onSuccess: (_) async {\n    final user = await CometChatUIKit.getLoggedInUser(); // Unnecessary!\n    if (user != null) { ... }\n  },\n);\n\n// ❌ ALSO WRONG — raw SDK getLoggedInUser (bypasses UIKit, unreliable)\nUser? existingUser = await CometChat.getLoggedInUser();\n```\n\n## Rule: SCAFFOLD_NO_RESIZE\n\nAny `Scaffold` containing `CometChatMessageComposer` MUST set `resizeToAvoidBottomInset: false`. The composer handles keyboard spacing internally via `SliverSpacing`. Leaving it `true` causes double-compensation and layout jumps.\n\n```dart\n// ✅ CORRECT\nScaffold(\n  resizeToAvoidBottomInset: false,\n  body: Column(\n    children: [\n      Expanded(child: CometChatMessageList(user: user)),\n      CometChatMessageComposer(user: user),\n    ],\n  ),\n)\n\n// ❌ WRONG — default is true, causes double keyboard compensation\nScaffold(\n  body: Column(\n    children: [\n      Expanded(child: CometChatMessageList(user: user)),\n      CometChatMessageComposer(user: user),\n    ],\n  ),\n)\n```\n\n## Rule: LISTENER_LIFECYCLE\n\nSDK listeners MUST be registered with a unique ID in `initState()` and removed with the same ID in `dispose()`. Forgetting removal causes duplicate events and memory leaks.\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\nclass _MyScreenState extends State<MyScreen> with MessageListener {\n  @override\n  void initState() {\n    super.initState();\n    CometChat.addMessageListener('messages', this); // Collision!\n  }\n  // Missing dispose → listener leaks\n}\n```\n\n## Rule: THEME_CACHE\n\nCache theme values in `didChangeDependencies()` with a `_themeInitialized` flag. Never call `CometChatThemeHelper.getColorPalette(context)` in `build()` — during keyboard animation, `MediaQuery` changes trigger rebuilds, and each lookup does expensive InheritedWidget traversal (44-95ms instead of <16ms).\n\n```dart\n// ✅ CORRECT — Hybrid pattern\nclass _MyWidgetState extends State<MyWidget> {\n  late CometChatColorPalette _colorPalette;\n  late CometChatSpacing _spacing;\n  late CometChatTypography _typography;\n  bool _themeInitialized = false;\n\n  @override\n  void didChangeDependencies() {\n    super.didChangeDependencies();\n    if (!_themeInitialized) {\n      _colorPalette = CometChatThemeHelper.getColorPalette(context);\n      _spacing = CometChatThemeHelper.getSpacing(context);\n      _typography = CometChatThemeHelper.getTypography(context);\n      _themeInitialized = true;\n    }\n  }\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\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// ❌ WRONG — no error, but presence events never fire\nUIKitSettingsBuilder()\n  ..appId = 'APP_ID'\n  ..region = 'us'\n```\n\n## Rule: REGION_LOWERCASE\n\nRegion must be a lowercase string. The SDK validates against `['us', 'eu', 'in']`.\n\n```dart\n// ✅ CORRECT\n..region = 'us'\n\n// ❌ WRONG — throws ERR_INVALID_REGION\n..region = 'US'\n```\n\n## Rule: SERVICE_LOCATOR_INIT\n\nEach component's `ServiceLocator.instance.setup()` must be called before creating its BLoC. The UIKit widgets do this automatically, but if you create BLoCs manually:\n\n```dart\n// ✅ CORRECT\nConversationsServiceLocator.instance.setup();\nfinal bloc = ConversationsBloc(\n  getLoggedInUserUseCase: ConversationsServiceLocator.instance.getLoggedInUserUseCase,\n  // ...\n);\n\n// ❌ WRONG — StateError: not initialized\nfinal bloc = ConversationsBloc(\n  getLoggedInUserUseCase: ConversationsServiceLocator.instance.getLoggedInUserUseCase,\n);\n```\n\n## Rule: MUID_PRESERVATION\n\nWhen sending messages, the SDK may return an empty `muid` in the success callback. The UIKit preserves the original `muid` for pending→sent deduplication. If you handle `ccMessageSent` events, compare by `muid` first, then `id`.\n\n## Pattern: Callback → Async Bridge\n\nThe CometChat SDK uses callback-based APIs (`onSuccess`/`onError`). Wrap them with `Completer` for async/await:\n\n```dart\nimport 'dart:async';\n\nFuture<User> loginAsync(String uid) {\n  final completer = Completer<User>();\n  CometChatUIKit.login(uid,\n    onSuccess: (user) => completer.complete(user),\n    onError: (e) => completer.completeError(e),\n  );\n  return completer.future;\n}\n\n// Usage\ntry {\n  final user = await loginAsync('user123');\n} on CometChatException catch (e) {\n  debugPrint('Login failed: ${e.message}');\n}\n```\n\nThis pattern is used internally by the UIKit's repository layer. Use it when calling SDK methods directly outside UIKit components.\n\n## Component Architecture Pattern\n\nEvery component follows this structure:\n\n```\n{component}/\n├── bloc/\n│   ├── {component}_bloc.dart      # Extends Bloc<Event, State>, registers SDK listeners\n│   ├── {component}_event.dart     # Equatable events\n│   └── {component}_state.dart     # Equatable state with copyWith\n├── domain/\n│   ├── usecases/                  # One class per operation\n│   └── repositories/              # Abstract interface\n├── data/\n│   ├── repositories/              # Impl delegates to datasource\n│   └── datasources/               # SDK calls\n├── di/\n│   └── {component}_service_locator.dart  # Singleton, setup() method\n└── widgets/                       # UI, uses BlocConsumer/BlocBuilder\n```\n\n## Naming Conventions\n\n| Type | Pattern | Example |\n|------|---------|---------|\n| Widget | `CometChat{Name}` | `CometChatConversations` |\n| BLoC | `{Name}Bloc` | `ConversationsBloc` |\n| Event | `{Verb}{Name}` | `LoadConversations`, `MessageReceived` |\n| State | `{Name}State` | `ConversationsLoaded`, `MessageListState` |\n| Repository | `{Name}Repository` / `{Name}RepositoryImpl` | `ConversationsRepository` |\n| Use Case | `{Verb}{Name}UseCase` | `GetConversationsUseCase` |\n| Service Locator | `{Name}ServiceLocator` | `ConversationsServiceLocator` |\n| Style | `CometChat{Name}Style` | `CometChatConversationsStyle` |\n\n## Checklist — Every CometChat Screen\n\n- [ ] `CometChatUIKit.init()` called before any usage\n- [ ] Auth check uses `CometChatUIKit.loggedInUser` after init, not `CometChat.getLoggedInUser()`\n- [ ] `subscriptionType` set in UIKitSettingsBuilder\n- [ ] `region` is lowercase\n- [ ] Scaffold has `resizeToAvoidBottomInset: false` if composer is present\n- [ ] Theme cached in `didChangeDependencies()`, not `build()`\n- [ ] SDK listeners registered with unique ID, removed in `dispose()`\n- [ ] Colors from `CometChatThemeHelper`, never hardcoded\n- [ ] Strings from `Translations.of(context)`, never hardcoded","tags":["cometchat","flutter","core","skills","agent-skills","ai-agent","chat","claude-code","cursor","messaging","nextjs","react"],"capabilities":["skill","source-cometchat","skill-cometchat-flutter-v6-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-v6-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 (9,302 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:51.283Z","embedding":null,"createdAt":"2026-05-07T13:05:11.033Z","updatedAt":"2026-05-18T19:04:51.283Z","lastSeenAt":"2026-05-18T19:04:51.283Z","tsv":"'-95':476 '16ms':480 '44':475 'abstract':801 'alreadi':132 'also':240,254,279 'anim':463 'api':697 'app':80,573 'appid':79,572 'appli':241 'architectur':766 'async':173,256,270,688,709 'async/await':705 'auth':85,117,876 'authkey':84 'automat':624 'await':90,273,289,733 'base':168,213,696 'bloc':618,629,635,644,774,778,831,833 'bloc.dart':776 'blocconsumer/blocbuilder':821 'bodi':326,346 'bool':498 'bridg':262,689 'bug':26 'build':89,460,521,526,904 'buildcontext':527 'bypass':284 'cach':36,136,445,446,900 'call':73,145,175,196,204,231,251,456,614,758,811,872 'callback':212,236,664,687,695 'callback-bas':211,694 'case':852 'catch':738 'caus':55,314,341,381,420,522 'ccmessages':678 'chang':465 'chat':14 'check':118,142,151,877 'checklist':867 'child':330,350 'children':328,348 'class':389,425,485,797 'code':10,52 'collis':421,438 'color':530,536,914 'colorpalett':491,507 'colors.primary':537 'column':327,347 'cometchat':2,13,39,50,229,691,828,863,869 'cometchat-flutter-v6-core':1 'cometchat.addmessagelistener':408,435 'cometchat.getloggedinuser':146,182,290,883 'cometchat.removemessagelistener':414 'cometchatcolorpalett':490 'cometchatconvers':830 'cometchatconversationsstyl':866 'cometchatexcept':737 'cometchatmessagecompos':298,334,354 'cometchatmessagelist':331,351 'cometchatspac':493 'cometchatsubscriptiontype.allusers':88,562 'cometchatthemehelp':916 'cometchatthemehelper.getcolorpalette':457,508,531 'cometchatthemehelper.getspacing':511 'cometchatthemehelper.gettypography':514 'cometchattypographi':496 'cometchatuikit.getloggedinuser':274 'cometchatuikit.init':28,63,91,109,122,155,178,266,871 'cometchatuikit.loggedinuser':130,161,200,249,879 'cometchatuikit.login':112,717 'compar':680 'compens':317,344 'complet':65,108,123,154,703,715,716 'completer.complete':721 'completer.completeerror':725 'completer.future':728 'compon':69,609,764,765,769,773,775,784,788,813 'compos':304,896 'condit':115 'consist':238 'constraint':47 'contain':16,297,535 'context':458,509,512,515,528,532,922 'convent':823 'conversationsbloc':636,645,834 'conversationsload':843 'conversationsrepositori':850 'conversationsserviceloc':861 'conversationsservicelocator.instance.getloggedinuserusecase':638,647 'conversationsservicelocator.instance.setup':633 'copywith':793 'core':5,42 'correct':75,149,322,388,482,559,594,632 'cover':27 'crash':23,59 'creat':616,628 'dart':74,148,253,321,387,481,558,593,631,706,708 'data':803 'datasourc':808,809 'datetime.now':406 'debugprint':95,100,740 'dedupl':674 'default':338 'deleg':806 'di':812 'didchangedepend':450,503,902 'direct':761 'disabl':547 'dispos':378,413,423,440,913 'domain':794 'done':97 'doubl':316,342 'double-compens':315 'duplic':382 'e':99,191,724,726,739 'e.message':103,743 'empti':659 'equat':786,790 'err':599 'error':555,565 'eu':591 'event':383,550,568,679,779,787,835 'event.dart':785 'everi':768,868 'exampl':826 'exist':138,189,221 'existingus':288 'expand':329,349 'expens':472,533 'extend':391,427,487,777 'fail':102,217,742 'failur':22,57 'fals':302,325,500,894 'field':129 'final':76,159,271,396,529,634,643,714,731 'fire':202,237,570 'first':62,683 'flag':454 'flutter':3,40 'follow':770 'forget':379 'futur':710 'getconversationsusecas':856 'getloggedinus':174,197,257,283 'getloggedinuserusecas':637,646 'handl':305,677 'hard':17 'hardcod':418,918,924 'hasus':160,170 'home':165 'hybrid':483 'id':81,368,376,419,574,685,910 'impl':805 'import':707 'indic':553 'inheritedwidget':473 'init':61,96,101,107,120,153,177,193,259,607,881 'initi':642 'initst':370,401,433 'instead':478 'interfac':802 'intern':195,308,748 'invalid':600 'jank':523 'jump':320 'key':86 'keyboard':306,343,462 'late':395,489,492,495 'layer':754 'layout':319 'leak':386,442 'leav':311 'lifecycl':34,359 'listen':33,358,361,441,783,906 'listenerid':398,403,409,415 'loadconvers':838 'locat':606,858 'log':224,226 'login':29,68,105,167,243,741 'loginasync':711,734 'loginwithauthtoken':245 'logout':30 'lookup':470,519 'lowercas':579,584,890 'manual':630 'may':656 'mediaqueri':464 'memori':385 'messag':436,653 'messagelisten':394,430 'messagelistst':844 'messagereceiv':839 'method':194,233,760,817 'millisecondssinceepoch':407 'miss':422,439 'ms':477 'muid':649,660,670,682 'must':64,299,362,581,612 'myscreenst':390,426 'mywidgetst':486 'name':822,829,832,837,841,846,848,854,859,864 'nativ':261 'negoti':46 'neither':235 'never':455,569,917,923 'non':45 'non-negoti':44 'null':162,278 'omit':542 'one':796 'onerror':98,190,699,723 'online/offline':551 'onsuccess':94,126,158,181,183,203,252,269,698,719 'oper':799 'origin':669 'outsid':762 'overrid':399,411,431,501,524 'pattern':484,686,745,767,825 'pend':672 'per':798 'pleas':225 'popul':133,248 'presenc':549,567 'present':898 'preserv':650,667 'prevent':20 'race':114 'raw':281 'rebuild':467 'redund':208,260 'region':82,575,578,580,595,601,602,888 'regist':364,781,907 'remov':372,380,424,911 'repositori':753,800,804,845,847 'repositoryimpl':849 'requir':541 'resiz':294 'resizetoavoidbottomin':38 'resizetoavoidbottominset':301,324,893 'return':534,657,727 'round':264 'round-trip':263 'rout':163 'rule':18,43,60,116,291,357,443,538,577,604,648 'scaffold':37,292,296,323,345,891 'screen':405,870 'sdk':72,223,282,360,587,655,692,759,782,810,905 'send':652 'sent':673 'separ':147,172 'servic':605,857 'service_locator.dart':814 'serviceloc':860 'servicelocator.instance.setup':611 'session':137,188,220 'set':77,93,111,157,180,199,268,300,885 'setup':816 'silent':21,56,216,546 'singleton':815 'skill' 'skill-cometchat-flutter-v6-core' 'sliverspac':310 'source-cometchat' 'space':307,494,510 'state':392,428,488,780,791,840,842 'state.dart':789 'stateerror':640 'static':128 'string':397,585,712,919 'structur':772 'style':862,865 'subscript':539 'subscriptiontyp':87,543,561,884 'subtl':25 'success':663 'super.didchangedependencies':504 'super.dispose':416 'super.initstate':402,434 'synchron':141,150 'theme':35,444,447,899 'themeiniti':453,499,506,516 'three':247 'throw':598 'thrown':557 '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' 'translations.of':921 'travers':474 'tri':730 'trigger':466 'trip':265 'true':313,340,517 'type':540,552,824 'typographi':497,513 'ui':819 'uid':113,713,718 'uikit':15,41,51,285,620,666,751,763 'uikitset':31,92,110,156,179,267 'uikitsettingsbuild':32,78,545,560,571,887 'uniqu':367,909 'unnecessari':275 'unreli':185,286 'us':83,576,590,596,603 'usag':70,729,875 'use':6,12,139,693,747,755,820,851,878 'usecas':795,855 'user':184,272,277,287,332,333,335,336,352,353,355,356,720,722,732 'user123':735 'v6':4 'valid':588 'valu':448 'verb':836,853 'version':214 'via':309 'violat':53 'void':400,412,432,502 'widget':525,621,818,827 'wrap':700 'write':8 'wrong':104,171,255,280,337,417,518,563,597,639","prices":[{"id":"fcddd43b-dc16-4dcd-9b09-4d0ed7b5ba36","listingId":"992cddc5-bcd4-49cc-99d7-10ebd559db83","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:11.033Z"}],"sources":[{"listingId":"992cddc5-bcd4-49cc-99d7-10ebd559db83","source":"github","sourceId":"cometchat/cometchat-skills/cometchat-flutter-v6-core","sourceUrl":"https://github.com/cometchat/cometchat-skills/tree/main/skills/cometchat-flutter-v6-core","isPrimary":false,"firstSeenAt":"2026-05-07T13:05:11.033Z","lastSeenAt":"2026-05-18T19:04:51.283Z"}],"details":{"listingId":"992cddc5-bcd4-49cc-99d7-10ebd559db83","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"cometchat","slug":"cometchat-flutter-v6-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":"5cffcc3a060756479373e89aafd17b48dcdf4431","skill_md_path":"skills/cometchat-flutter-v6-core/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/cometchat/cometchat-skills/tree/main/skills/cometchat-flutter-v6-core"},"layout":"multi","source":"github","category":"cometchat-skills","frontmatter":{"name":"cometchat-flutter-v6-core","license":"MIT","description":"Use when writing any code that uses cometchat_chat_uikit. Contains hard rules that prevent silent failures, crashes, and subtle bugs. Covers CometChatUIKit.init, login, logout, UIKitSettings, UIKitSettingsBuilder, listener lifecycle, theme caching, Scaffold resizeToAvoidBottomInset, subscriptionType, region, muid preservation, and the Clean Architecture + BLoC component pattern. Also use when seeing errors like \"Authentication null\", \"APP ID null\", ERR_ALREADY_LOGGED_IN, or StateError from uninitialized ServiceLocator. Make sure to use this skill for any CometChat Flutter UIKit code, even simple widget usage.","compatibility":"cometchat_chat_uikit ^6.0.0-beta2; flutter_bloc ^8.1.0"},"skills_sh_url":"https://skills.sh/cometchat/cometchat-skills/cometchat-flutter-v6-core"},"updatedAt":"2026-05-18T19:04:51.283Z"}}