CometChat Flutter UIKit v5 — Core Rules
SkillDev toolsUse 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.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the CometChat Flutter UIKit v5 — Core Rules skill
What this skill tells your AI
The instructions your AI receives, as published by cometchat/cometchat-skills in skills/cometchat-flutter-v5-core/SKILL.md and read by ahel’s review.
Ground truth:
cometchat_chat_uikit: ^5.2(GetX-based; pair withcometchat_calls_uikit: ^5.0/ rawcometchat_calls_sdk ^5.0.2for calls) — the pub-cache package source +docs/ui-kit/flutter/v5. Official docs: https://www.cometchat.com/docs/ui-kit/flutter/v5/overview · Docs MCP:claude mcp add --transport http cometchat-docs https://www.cometchat.com/docs/mcp(or fetch the URL directly without MCP). V5 is legacy/maintenance-only (V6 is current); verify Dart symbols against the resolved package source.
Non-negotiable constraints for all CometChat UIKit v5 code. Violating these causes silent failures or crashes.
Key v5 Architecture Facts
- State management: GetX (GetBuilder, GetxController, Get.put, Get.find, Get.delete)
- Separate packages:
cometchat_chat_uikit+cometchat_calls_uikit+cometchat_uikit_shared - SDK:
cometchat_sdk ^4.1.2+cometchat_calls_sdk ^4.2.2(chat-kit baseline). For voice/video calling, do NOT usecometchat_calls_uikit(4.x-bound) — integrate the rawcometchat_calls_sdk ^5.0.2percometchat-flutter-v5-calls(the V5-canonical calls path). - Imports — two barrels. For chat-only projects:
package:cometchat_chat_uikit/cometchat_chat_uikit.dart. For projects that also need voice/video calling: ADDpackage:cometchat_calls_uikit/cometchat_calls_uikit.dartas a SECOND import — the calls barrel re-exports shared + SDK only and does NOT re-exportcometchat_chat_uikit. Chat widgets likeCometChatConversations,CometChatMessageList,CometChatMessageComposerare reachable only through the chat barrel. CometChatUIKit.login(uid)takes a String directly (not an object)- No ServiceLocator pattern — controllers are created via
Get.put()internally - Style classes use
ThemeExtensionwithmerge()pattern
Rule: INIT_FIRST
CometChatUIKit.init() must complete before any login, component usage, or SDK call.
// ✅ CORRECT
final settings = (UIKitSettingsBuilder()
..appId = 'APP_ID'
..region = 'us'
..authKey = 'AUTH_KEY'
..subscriptionType = CometChatSubscriptionType.allUsers)
.build();
CometChatUIKit.init(
uiKitSettings: settings,
onSuccess: (_) => debugPrint('Init done'),
onError: (e) => debugPrint('Init failed: ${e.message}'),
);
// ❌ WRONG — login before init completes
CometChatUIKit.init(uiKitSettings: settings);
CometChatUIKit.login('uid'); // Race condition
Rule: AUTH_CHECK_AFTER_INIT
After 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.
// ✅ CORRECT — synchronous check after init
CometChatUIKit.init(
uiKitSettings: settings,
onSuccess: (_) {
final hasUser = CometChatUIKit.loggedInUser != null;
// Route to home or login
},
);
// ✅ ALSO CORRECT — explicit async check (used by master app)
CometChatUIKit.init(
uiKitSettings: settings,
onSuccess: (_) async {
final user = await CometChatUIKit.getLoggedInUser();
if (user != null) {
await CometChatUIKit.login(user.uid, onSuccess: ...);
}
},
);
Note: 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.
Rule: LISTENER_LIFECYCLE
SDK listeners MUST be registered with a unique ID in initState() (or GetxController onInit()) and removed in dispose() (or onClose()).
// ✅ CORRECT
class _MyScreenState extends State<MyScreen> with MessageListener {
late final String _listenerId;
@override
void initState() {
super.initState();
_listenerId = 'my_screen_${DateTime.now().millisecondsSinceEpoch}';
CometChat.addMessageListener(_listenerId, this);
}
@override
void dispose() {
CometChat.removeMessageListener(_listenerId);
super.dispose();
}
}
// ❌ WRONG — hardcoded ID causes collisions; missing dispose removal
CometChat.addMessageListener('messages', this); // Collision!
Rule: THEME_CACHE
Cache theme values in didChangeDependencies() — unconditionally, no flag needed. Never call CometChatThemeHelper.getColorPalette(context) in build().
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().
// ✅ CORRECT — matches actual package pattern (no flag)
@override
void didChangeDependencies() {
super.didChangeDependencies();
colorPalette = CometChatThemeHelper.getColorPalette(context);
spacing = CometChatThemeHelper.getSpacing(context);
typography = CometChatThemeHelper.getTypography(context);
}
// ❌ WRONG — lookup in build causes jank
@override
Widget build(BuildContext context) {
final colors = CometChatThemeHelper.getColorPalette(context); // Expensive!
return Container(color: colors.primary);
}
Do NOT use a _themeInitialized flag — it prevents theme updates when the system switches between light/dark mode.
Rule: SUBSCRIPTION_TYPE_REQUIRED
Omitting subscriptionType in UIKitSettingsBuilder silently disables all presence events (online/offline, typing indicators). No error is thrown.
// ✅ CORRECT
UIKitSettingsBuilder()
..subscriptionType = CometChatSubscriptionType.allUsers
Rule: REGION_LOWERCASE
Region must be a lowercase string: 'us', 'eu', or 'in'.
Rule: MUID_PRESERVATION
When handling ccMessageSent events, compare by muid first, then id — the SDK may return an empty muid in the success callback.
Pattern: Callback → Async Bridge (Completer)
import 'dart:async';
Future<bool> initAsync(UIKitSettings settings) {
final completer = Completer<bool>();
CometChatUIKit.init(
uiKitSettings: settings,
onSuccess: (_) => completer.complete(true),
onError: (e) => completer.complete(false),
);
return completer.future;
}
v5 Component Architecture Pattern (GetX)
{component}/
├── cometchat_{component}.dart # StatefulWidget
├── cometchat_{component}_controller.dart # extends GetxController
├── cometchat_{component}_style.dart # ThemeExtension with merge()
└── {component}_builder_protocol.dart # Request builder protocol
Internal lifecycle:
@override
void initState() {
super.initState();
tag = widget.controllerTag ?? 'default_tag_${DateTime.now().millisecondsSinceEpoch}';
controller = Get.put<Controller>(Controller(...), tag: tag);
}
@override
void dispose() {
if (widget.controllerTag == null) {
Get.delete<Controller>(tag: tag);
}
super.dispose();
}
Android Build Requirements
android.useAndroidX=trueandandroid.enableJetifier=trueingradle.propertiesminSdk 26inandroid/app/build.gradle- ProGuard:
-keep class com.cometchat.** { *; }and-keep interface com.cometchat.** { *; }
Top 10 Error Debugging
| Symptom | Cause | Fix |
|---|---|---|
| "Authentication null" | CometChatUIKit.init() not called | Call init before login/components |
| "APP ID null" | appId not set in UIKitSettingsBuilder | Set ..appId = 'YOUR_APP_ID' |
| No typing indicators / presence | subscriptionType not set | Set ..subscriptionType = CometChatSubscriptionType.allUsers |
| Theme jank during keyboard | Theme looked up in build() | Cache in didChangeDependencies() |
| Listener leak / duplicate events | Listener not removed in dispose() | Always remove with same ID |
| GetX controller not found | Using Get.find() before Get.put() | Let UIKit components manage their own controllers |
| Region error | Uppercase region string | Use lowercase: 'us', 'eu', 'in' |
| Release build crash | Missing ProGuard keep rules | Add -keep class com.cometchat.** { *; } |
Checklist — Every CometChat v5 Screen
-
CometChatUIKit.init()called before any usage -
subscriptionTypeset in UIKitSettingsBuilder -
regionis lowercase - Theme cached in
didChangeDependencies(), notbuild() - SDK listeners registered with unique ID, removed in
dispose() - Colors from
CometChatThemeHelper, never hardcoded - Imports:
package:cometchat_chat_uikit/cometchat_chat_uikit.dartalways; ADDpackage:cometchat_calls_uikit/cometchat_calls_uikit.dartif you use voice/video
Visual Builder integration
Flutter V5 is the primary home for Visual Builder integration. The canonical repo at the chat_builder/ directory inside the Flutter Visual Builder ZIP (download from https://preview.cometchat.com/downloads/cometchat-builder-flutter.zip) ships V5-shaped code — cometchat_chat_uikit: ^5.2.12 + cometchat_calls_uikit: ^5.0.13. The integration copies the entire chat_builder/ directory as a path: dependency, then BuilderSettingsHelper.loadFromAsset() reads chat_builder/assets/sample_app/cometchat-builder-settings.json and configures the bundled chat UI accordingly.
The full recipe lives in cometchat-flutter-v6-core §"Visual Builder integration" because that's where the V6-prep restructure originally landed the validated content. Both skills reference the same canonical; the V6 page carries a "V5-shaped code" warning at the top. V5 customers should follow that recipe AS-IS — the canonical IS V5-targeted.
Validated 2026-05-21 against Flutter 3.38.3: flutter build apk --debug produces app-debug.apk after applying:
- Envelope-wrapped JSON at
chat_builder/assets/sample_app/cometchat-builder-settings.json. The CLI (source of truth) writes{ builderId, name, settings: {...} }; the vendor canonical's own checked-in file carries only{ builderId, settings }(noname).BuilderSettingsHelper.loadFromAsset()reads onlybuilderId+settingsand ignores any extra top-level keys, so the CLI's extranameis harmless. - Two missing-field defaults injected pre-write (mentionAll + inAppSounds — same as Android)
android.enableJetifier=trueinandroid/gradle.properties(the chat SDK pullscom.android.supporttransitively)await BuilderSettingsHelper.loadFromAsset()inlib/main.dartbeforerunApp()chat_builder: { path: ./chat_builder }in hostpubspec.yaml
Differences from the V6 page's recipe text:
- V5 host code uses
StatefulWidgetwith direct listener management (V6 uses BLoC pattern); both work with the embedded chat_builder package since it owns its own state. - V5 calls (the standalone
cometchat-flutter-v5-callsflow, outside the builder) don't need the [[project_v6_flutter_calls_partial]] navigatorKey workaround — that's a V6-specific incoming-call-routing requirement. However, the Visual Builder canonical itself (V5-shaped) wiresnavigatorKey: CallNavigationContext.navigatorKeyUNCONDITIONALLY in everyMaterialAppChatBuilder.createApp()builds, regardless of whether calls are enabled — so if you mountChatBuilder.createApp()(recommended) or replicate itsMaterialAppin a host wrapper, keep that navigator key in place. Do NOT gate it behind "calls enabled". See the v6-core recipe's wrapper guidance.
Signals
- GitHub stars
- 105
- Forks
- 2
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
cometchat-flutter-v5-core- Source
- github.com/cometchat/cometchat-skills