cometchat-flutter-v6-core
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
What it does
CometChat Flutter UIKit — Core Rules
Non-negotiable constraints for all CometChat UIKit code. Violating these causes silent failures or crashes.
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();
await 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 (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.
// ✅ CORRECT — synchronous check after init completes
CometChatUIKit.init(
uiKitSettings: settings,
onSuccess: (_) {
final hasUser = CometChatUIKit.loggedInUser != null;
// Route to home or login based on hasUser
},
);
// ❌ WRONG — separate async getLoggedInUser call after init
CometChatUIKit.init(
uiKitSettings: settings,
onSuccess: (_) {
CometChat.getLoggedInUser(
onSuccess: (user) { ... }, // Unreliable when no session exists
onError: (e) { ... },
);
},
);
The 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).
This also applies to login() and loginWithAuthToken() — all three populate CometChatUIKit.loggedInUser before calling onSuccess.
// ❌ ALSO WRONG — async getLoggedInUser after init (redundant native bridge round-trip)
CometChatUIKit.init(
uiKitSettings: settings,
onSuccess: (_) async {
final user = await CometChatUIKit.getLoggedInUser(); // Unnecessary!
if (user != null) { ... }
},
);
// ❌ ALSO WRONG — raw SDK getLoggedInUser (bypasses UIKit, unreliable)
User? existingUser = await CometChat.getLoggedInUser();
Rule: SCAFFOLD_NO_RESIZE
Any Scaffold containing CometChatMessageComposer MUST set resizeToAvoidBottomInset: false. The composer handles keyboard spacing internally via SliverSpacing. Leaving it true causes double-compensation and layout jumps.
// ✅ CORRECT
Scaffold(
resizeToAvoidBottomInset: false,
body: Column(
children: [
Expanded(child: CometChatMessageList(user: user)),
CometChatMessageComposer(user: user),
],
),
)
// ❌ WRONG — default is true, causes double keyboard compensation
Scaffold(
body: Column(
children: [
Expanded(child: CometChatMessageList(user: user)),
CometChatMessageComposer(user: user),
],
),
)
Rule: LISTENER_LIFECYCLE
SDK 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.
// ✅ 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
class _MyScreenState extends State<MyScreen> with MessageListener {
@override
void initState() {
super.initState();
CometChat.addMessageListener('messages', this); // Collision!
}
// Missing dispose → listener leaks
}
Rule: THEME_CACHE
Cache 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).
// ✅ CORRECT — Hybrid pattern
class _MyWidgetState extends State<MyWidget> {
late CometChatColorPalette _colorPalette;
late CometChatSpacing _spacing;
late CometChatTypography _typography;
bool _themeInitialized = false;
@override
void didChangeDependencies() {
super.didChangeDependencies();
if (!_themeInitialized) {
_colorPalette = CometChatThemeHelper.getColorPalette(context);
_spacing = CometChatThemeHelper.getSpacing(context);
_typography = CometChatThemeHelper.getTypography(context);
_themeInitialized = true;
}
}
}
// ❌ WRONG — lookup in build causes jank
@override
Widget build(BuildContext context) {
final colors = CometChatThemeHelper.getColorPalette(context); // Expensive!
return Container(color: colors.primary);
}
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
// ❌ WRONG — no error, but presence events never fire
UIKitSettingsBuilder()
..appId = 'APP_ID'
..region = 'us'
Rule: REGION_LOWERCASE
Region must be a lowercase string. The SDK validates against ['us', 'eu', 'in'].
// ✅ CORRECT
..region = 'us'
// ❌ WRONG — throws ERR_INVALID_REGION
..region = 'US'
Rule: SERVICE_LOCATOR_INIT
Each component's ServiceLocator.instance.setup() must be called before creating its BLoC. The UIKit widgets do this automatically, but if you create BLoCs manually:
// ✅ CORRECT
ConversationsServiceLocator.instance.setup();
final bloc = ConversationsBloc(
getLoggedInUserUseCase: ConversationsServiceLocator.instance.getLoggedInUserUseCase,
// ...
);
// ❌ WRONG — StateError: not initialized
final bloc = ConversationsBloc(
getLoggedInUserUseCase: ConversationsServiceLocator.instance.getLoggedInUserUseCase,
);
Rule: MUID_PRESERVATION
When 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.
Pattern: Callback → Async Bridge
The CometChat SDK uses callback-based APIs (onSuccess/onError). Wrap them with Completer for async/await:
import 'dart:async';
Future<User> loginAsync(String uid) {
final completer = Completer<User>();
CometChatUIKit.login(uid,
onSuccess: (user) => completer.complete(user),
onError: (e) => completer.completeError(e),
);
return completer.future;
}
// Usage
try {
final user = await loginAsync('user123');
} on CometChatException catch (e) {
debugPrint('Login failed: ${e.message}');
}
This pattern is used internally by the UIKit's repository layer. Use it when calling SDK methods directly outside UIKit components.
Component Architecture Pattern
Every component follows this structure:
{component}/
├── bloc/
│ ├── {component}_bloc.dart # Extends Bloc<Event, State>, registers SDK listeners
│ ├── {component}_event.dart # Equatable events
│ └── {component}_state.dart # Equatable state with copyWith
├── domain/
│ ├── usecases/ # One class per operation
│ └── repositories/ # Abstract interface
├── data/
│ ├── repositories/ # Impl delegates to datasource
│ └── datasources/ # SDK calls
├── di/
│ └── {component}_service_locator.dart # Singleton, setup() method
└── widgets/ # UI, uses BlocConsumer/BlocBuilder
Naming Conventions
| Type | Pattern | Example |
|---|---|---|
| Widget | CometChat{Name} | CometChatConversations |
| BLoC | {Name}Bloc | ConversationsBloc |
| Event | {Verb}{Name} | LoadConversations, MessageReceived |
| State | {Name}State | ConversationsLoaded, MessageListState |
| Repository | {Name}Repository / {Name}RepositoryImpl | ConversationsRepository |
| Use Case | {Verb}{Name}UseCase | GetConversationsUseCase |
| Service Locator | {Name}ServiceLocator | ConversationsServiceLocator |
| Style | CometChat{Name}Style | CometChatConversationsStyle |
Checklist — Every CometChat Screen
-
CometChatUIKit.init()called before any usage - Auth check uses
CometChatUIKit.loggedInUserafter init, notCometChat.getLoggedInUser() -
subscriptionTypeset in UIKitSettingsBuilder -
regionis lowercase - Scaffold has
resizeToAvoidBottomInset: falseif composer is present - Theme cached in
didChangeDependencies(), notbuild() - SDK listeners registered with unique ID, removed in
dispose() - Colors from
CometChatThemeHelper, never hardcoded - Strings from
Translations.of(context), never hardcoded
Capabilities
Install
Quality
deterministic score 0.46 from registry signals: · indexed on github topic:agent-skills · 27 github stars · SKILL.md body (9,302 chars)