cometchat-flutter-v6-calls — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited cometchat-flutter-v6-calls (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Production-grade voice + video calling for Flutter UIKit v6 (stable, Bloc-based). Loaded by cometchat-calls when framework === "flutter" and flutter_version === "v6". Operates in two modes:
cometchat_chat_uikit) — no extra dependency. The skill enables calling on UIKitSettings, calls CometChatUIKitCalls.init() in the chat-init success callback, mounts the global incoming-call overlay at app root.Read these other skills first:
cometchat-calls — dispatcher (modes, hard rules, anti-patterns)cometchat-flutter-v6-core — UIKitSettings, init/login order (CHAT_INIT_BEFORE_CALLS_INIT is THE rule)cometchat-flutter-v6-events — Bloc event streams + listener registrationV6 vs V5 difference (critical for migration):
cometchat_chat_uikit package (calls bundled in)cometchat_chat_uikit + cometchat_calls_uikit (separate)CometChatUIKitCalls.init(appId, region) must run AFTER CometChatUIKit.init() succeeds; V5 hides this via CometChatCallingExtensionGround truth:
[email protected] artifacts under ~/.pub-cache/calls-sdk-flutter-5/sample-apps/ (V5 sample; V6 sample app may not exist yet — verify before citing)enableCalls: true (ENG-35699)Single source of truth, verified against installed `cometchat_chat_uikit-6.0.1` source on 2026-06-01: when you set..enableCalls = trueonUIKitSettingsBuilderAND useCometChatUIKit.init/login(i.e. the V6 UIKit shape, the recommended path), the kit's `CallEventService` calls `CometChatCalls.init()` AND `CometChatCalls.loginWithAuthToken(...)` for you internally — once Chat SDK login resolves, the kit hands the auth token to the Calls SDK and brings it online. You do NOT need to callCometChatCalls.loginyourself. Seelib/call_ui/src/call_event_service.dartlines 87-197 in the installed package.
>
The earlier version of this skill said "Same as v5 cohort — you MUST also callCometChatCalls.login." That's wrong for V6 UIKit usage. It IS correct only when the integration is raw SDK (no UIKit) — i.e. the developer importedcometchat_calls_sdkdirectly without going throughCometChatUIKit. Most integrations don't.
The V6 UIKit recipe (recommended — 99% of integrations):
final settings = (UIKitSettingsBuilder()
..subscriptionType = CometChatSubscriptionType.allUsers
..region = REGION
..appId = APP_ID
..authKey = AUTH_KEY
..enableCalls = true) // <-- this flag wires CallEventService
.build();
CometChatUIKit.init(
uiKitSettings: settings,
onSuccess: (_) async {
await CometChatUIKit.login(uid); // kit logs Chat SDK
// Calls SDK login fires INTERNALLY here via CallEventService.
// No manual CometChatCalls.login needed.
},
onError: (e) { /* surface */ },
);Raw-SDK fallback (only if NOT using `CometChatUIKit`):
// You should rarely need this in V6 — only when you've opted out of the UIKit
// entirely and are wiring chat-sdk + calls-sdk by hand.
import 'package:cometchat_calls_sdk/cometchat_calls_sdk.dart';
// Chat SDK login is POSITIONAL (cometchat.dart:843 — login(String uid, String authKey, {onSuccess, onError}))
await CometChat.login(uid, AUTH_KEY, onSuccess: (User u) {}, onError: (CometChatException e) {});
// Calls SDK login uses NAMED params; onError is CometChatCallsException (cometchatcalls.dart:224-228)
CometChatCalls.login(
uid: uid,
authKey: AUTH_KEY,
onSuccess: (User? callUser) { /* both ready */ },
onError: (CometChatCallsException e) { /* surface */ },
);Surprises:
enableCalls = true but Chat-SDK login never fires (init error, network), call buttons silently render but the internal CometChatCalls.startSession flow throws "auth token cannot be null" (there is no startCall method — the lifecycle is startSession/joinSession). The fix is to surface the Chat SDK login error, not to add a manual CometChatCalls.login.references/add-calls-to-existing-chat.md (additive-mode recipe) shows the same canonical pattern.CometChatUIKitCalls.init after CometChatUIKit.initThe V6 UI Kit unifies the two SDKs but init order is still load-bearing:
// ✓ RIGHT — calls init in chat init's onSuccess
CometChatUIKit.init(
uiKitSettings: settings,
onSuccess: (_) {
CometChatUIKitCalls.init(appId, region,
onSuccess: (_) => debugPrint('Calls SDK ready'),
onError: (e) => debugPrint('Calls init failed: ${e.message}'),
);
},
);// ✗ WRONG — parallel init causes "auth token null" intermittently
CometChatUIKit.init(uiKitSettings: settings); // returns immediately
CometChatUIKitCalls.init(appId, region); // race — chat auth not ready yetCometChatUIKitCalls.init() must be called exactly once per app lifecycle. Re-calling it after logout + re-login causes "session already started" errors.
After logout: the calls SDK session is invalidated; on next login, call CometChatUIKitCalls.init() again. Treat this as a "first run" of the calls subsystem.
There is NO bundled `native_call_kit` module in `cometchat_chat_uikit` v6. (Verified against the publishedcometchat_chat_uikit6.0.1 source — nonative_call_kitdirectory, and noflutter_callkit_incoming/ CallKit / ConnectionService dependency in itspubspec.yaml.) VoIP push is wired with the SAME external packages the v5 family uses — the kit does not provide an OS-ring-UI module of its own.
Wire VoIP push with:
The Dart-side handler decodes the incoming-call payload from FCM/PushKit, shows the ring UI via flutter_callkit_incoming, and on accept routes into the kit's call surface. In additive mode this is opt-in; in standalone, mandatory.
⚠️ Android build prerequisite — Jetifier is mandatory. In V6 calls are bundled in cometchat_chat_uikit (there is no separate cometchat_calls_uikit package); one of its transitive deps still pulls in the legacy com.android.support:support-compat:26.1.0 AAR. Without Jetifier, AGP fails with Duplicate class android.support.v4.* errors. Set in android/gradle.properties:
android.useAndroidX=true
android.enableJetifier=trueFlutter 3.x scaffolds omit enableJetifier=true by default — the build fails on first flutter build apk if you skip this.
Same as native Android / Flutter v5. The four FOREGROUND_SERVICE_* permissions plus MANAGE_OWN_CALLS / BIND_TELECOM_CONNECTION_SERVICE in android/app/src/main/AndroidManifest.xml. V6 raised Android minSdk to 26 (calls SDK in V6 raised the floor) — verify this is set in android/app/build.gradle.
cometchat-flutter-v6-production covers it. CometChatUIKit.loginWithAuthToken(token) for production, never loginWithAuthKey(uid, authKey).
Future<void> endCall(String sessionId) async {
await CometChatUIKitCalls.endSession(); // 1. end WebRTC + release tracks
await FlutterCallkitIncoming.endAllCalls(); // 2. clear OS-level ring UI
if (mounted) Navigator.of(context, rootNavigator: true).pop(); // 3. pop call screen
}After CometChatUIKit.logout(), also reset the calls service locator:
CallOperationsServiceLocator.instance.reset();The kit's call widgets handle the basic teardown automatically; custom WebRTC surfaces must replicate this.
permission_handler + CallPermissionsV6 ships an internal CallPermissions helper that wraps the standard permission flow:
final granted = await CallPermissions.requestMicrophoneAndCamera(); // video; use requestMicrophone() for audio-only
if (!granted) { /* surface a clear UI message */ }Native config (Info.plist + AndroidManifest) is identical to V5 (rule 1.6 in cometchat-flutter-v5-calls).
enableCalls = true (ENG-35698)Canonical truth from `cometchat_chat_uikit-6.0.1`: the kit'sCallEventServiceautomatically callsIncomingCallOverlay.show(...)fromlib/call_ui/src/incoming_call/cometchat_display_incoming_call_overlay.dartwhenever a foreground incoming call fires ANDenableCalls = trueis set onUIKitSettingsBuilder. You do NOT mount the overlay yourself. There is NOCometChatDisplayIncomingCallOverlaywidget — that was a fictional class name from earlier drafts. The real class isIncomingCallOverlay(incometchat_display_incoming_call_overlay.dart) and it's an imperative singleton (.show(...)/.dismiss()).
The correct V6 wiring is just the navigatorKey:
MaterialApp(
navigatorKey: CallNavigationContext.navigatorKey, // ⚠️ REQUIRED — see warning below
home: const AppRoot(), // your existing root
);That's it. No Stack, no builder wrapper, no mounting of an "overlay widget" — once enableCalls = true is in your UIKitSettingsBuilder, the kit's CallEventService shows + dismisses IncomingCallOverlay for you when foreground rings happen.
⚠️ `navigatorKey: CallNavigationContext.navigatorKey` is REQUIRED on MaterialApp — still active in v6.0.1 GA. The kit's CometChatCallButtons and outgoing-call flow navigate via CallNavigationContext.navigatorKey.currentContext. Without this line, CometChat.initiateCall succeeds (CALL-TRAP confirms onSuccess fires with a valid sessionId) but currentContext is null so CometChatOutgoingCall never mounts. Symptom: user taps call button, peer rings, but the Flutter app shows nothing. Note: the vendor's own 6.0.1 sample app (`examples/sample_app`) is broken for calls on mobile out-of-the-box — its main.dart sets navigatorKey: kIsWeb ? CallNavigationContext.navigatorKey : null, i.e. it wires the key only on web and passes null on Android/iOS, so the outgoing-call screen never mounts on a device (validated on Pixel 3). The kit's other sample (examples/ai_sample_app) sets it unconditionally (navigatorKey: navigatorKey + CallNavigationContext.navigatorKey = navigatorKey) — that is the correct, device-safe form this skill prescribes. Wire `navigatorKey: CallNavigationContext.navigatorKey` unconditionally (do NOT gate it on kIsWeb like sample_app does); don't copy sample_app's main.dart verbatim.
Import: import 'package:cometchat_chat_uikit/cometchat_calls_uikit.dart' show CallNavigationContext; (use show to avoid a name collision with kit-exported IncomingCallOverlay).
✅ Outgoing → in-call screen transition is FIXED in v6.0.1 GA. (It was broken in v6.0.0-beta2 — the outgoing-call screen stayed on "Calling…" indefinitely after the peer accepted.) Validated end-to-end 2026-05-27 on Pixel 3 with full CALL-TRAP instrumentation: with the navigatorKey wired (above), when the peer accepts, the kit's OutgoingCallBloc swaps the outgoing screen for the in-call surface automatically (via the kit's internal call-overlay — CallScreenOverlay.show()), transitioning from CometChatOutgoingCall ("Calling…") to the in-call view — observed call-duration timer ticking + WebRTC rendering frames @ ~27 fps. The v6.0.0 changelog's "Refreshed BLoC implementations across … call buttons, and ongoing call flows" was the fix. No client-side workaround needed beyond the navigatorKey wiring.
In standalone mode, flutter_callkit_incoming (the external package you add) owns the OS-level ring UI; the kit's in-app overlay only fires when the app is foregrounded.
# pubspec.yaml
dependencies:
flutter:
sdk: flutter
cometchat_chat_uikit: ^6.0 # calls bundled in
permission_handler: ^11.0.0
flutter_callkit_incoming: ^2.0.0 # standalone — VoIP UI bridge
firebase_messaging: ^14.0.0 # standalone — Android FCM
firebase_core: ^2.0.0V6 GA is on pub.dev — use the plain dependency above, NOT a Cloudsmith `hosted:` stanza. Cloudsmith (dart.cloudsmith.io/cometchat/cometchat/) only hosts the pre-GA6.0.0-beta*builds, so ahosted:install of^6.0.xfails with "version solving failed". The GA range resolves6.0.1/6.0.2/6.0.3from pub.dev. (Cloudsmith is only for the legacy V5 packages.)
Init (additive mode):
import 'package:cometchat_chat_uikit/cometchat_chat_uikit.dart';
import 'package:cometchat_chat_uikit/cometchat_calls_uikit.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final settings = (UIKitSettingsBuilder()
..appId = CometChatConfig.appId
..region = CometChatConfig.region
..authKey = CometChatConfig.authKey
..subscriptionType = CometChatSubscriptionType.allUsers)
.build();
CometChatUIKit.init(
uiKitSettings: settings,
onSuccess: (_) {
CometChatUIKitCalls.init(
CometChatConfig.appId,
CometChatConfig.region,
onSuccess: (_) => runApp(const MyApp()),
onError: (e) => runApp(MyErrorApp(e.message)),
);
},
onError: (e) => runApp(MyErrorApp(e.message)),
);
}The first import gives you chat components. The second gives you call-specific types.
Architecture (lives inside cometchat_chat_uikit):
call_ui/src/
├── call_buttons/ # CometChatCallButtons + CallButtonsBloc
├── incoming_call/ # CometChatIncomingCall + IncomingCallBloc
├── outgoing_call/ # CometChatOutgoingCall + OutgoingCallBloc
├── ongoing_call/ # CometChatOngoingCall + OngoingCallBloc
├── call_logs/ # CometChatCallLogs + CallLogsBloc + Clean Architecture
├── call_operations/ # Shared DI / use cases / repositories (CallOperationsServiceLocator)
├── call_bubble/ # CometChatCallBubble — call-event bubble in message list (auto-rendered)
├── call_settings/ # CometChatUIKitCalls, CallNavigationContext
├── utils/ # CallUtils, CallStateService, CallPermissions
├── call_event_service.dart # Centralized call event handling
└── calling_configuration.dart # Top-level config object| Widget | Purpose | Notes |
|---|---|---|
CometChatCallButtons(user:, group:) | Voice + video buttons | Mutually exclusive user / group. Use group: for group calls (meetings). |
CometChatIncomingCall(call:, user:, onAccept:, onDecline:) | In-app foreground ring UI | onAccept/onDecline are (BuildContext, Call) -> void. Custom view slots: titleView, subTitleView, leadingView, trailingView. ⚠️ Param-name quirk: this widget's settings param is callSettingsBuilder: (takes a SessionSettingsBuilder), NOT sessionSettingsBuilder: like Ongoing/Outgoing — verified lib/call_ui/src/incoming_call/cometchat_incoming_call.dart. |
CometChatOutgoingCall(call:, user:, outgoingCallStyle:) | Dialing UI | Auto-mounted when initiateCall is called via the kit. |
CometChatOngoingCall(sessionSettingsBuilder:, sessionId:, callWorkFlow:) | Active call view | Param is `sessionSettingsBuilder:` NOT `callSettingsBuilder:` (verified lib/call_ui/src/ongoing_call/cometchat_ongoing_call.dart constructor). callWorkFlow: CallWorkFlow.directCalling or CallWorkFlow.defaultCalling. Set resizeToAvoidBottomInset: false on host Scaffold. |
CometChatCallLogs(onItemClick:, callLogsStyle:) | Paginated history | Clean Architecture + BLoC; CallLogsServiceLocator is initialized automatically when the widget mounts. |
CometChatCallBubble | Call-event message bubble | Auto-rendered for call-type messages in CometChatMessageList. |
CometChatUIKitCalls API| Method | Purpose |
|---|---|
CometChatUIKitCalls.init(appId, region) | Initialize calls SDK (after chat init — rule 1.1) |
CometChatUIKitCalls.initiateCall(call) | Start a call (Chat SDK initiate + Calls SDK preflight) |
CometChatUIKitCalls.acceptCall(sessionId) | Accept incoming |
CometChatUIKitCalls.rejectCall(sessionId, status) | Reject / cancel |
CometChatUIKitCalls.generateToken(sessionId) | Mint session-scoped RTC token |
CometChatUIKitCalls.startSession(sessionId, settings) | Start WebRTC |
CometChatUIKitCalls.endSession() | End and cleanup |
CallingConfiguration — top-level configCallingConfiguration(
outgoingCallConfiguration: CometChatOutgoingCallConfiguration(...),
incomingCallConfiguration: CometChatIncomingCallConfiguration(...),
callButtonsConfiguration: CallButtonsConfiguration(...),
groupSessionSettingsBuilder: sessionSettingsBuilder,
)Pass to UIKitSettings.callingConfiguration to apply globally without per-component plumbing.
| Bloc | Events |
|---|---|
CallButtonsBloc | InitiateVoiceCall, InitiateVideoCall |
IncomingCallBloc | AcceptCall, RejectCall |
OutgoingCallBloc | CancelCall, CallAccepted(call), CallRejected(call) |
OngoingCallBloc | StartSession(sessionId, settings), EndSession |
CallLogsBloc | LoadCallLogs, LoadMoreCallLogs |
CallOperationsServiceLocator must be initialized before using call BLoCs directly — UIKit widgets handle this automatically. After logout, call CallOperationsServiceLocator.instance.reset() to clean up.
When product === "voice-video" and there is no v6 chat integration.
Split by calling mode:
Calls SDK ONLY. NO Chat SDK, NO UIKit. Same SDK as v5 (cometchat_calls_sdk). Scaffold:
cometchat_calls_sdk: ^5.0.0 + flutter_bloc + equatable + permission_handler ONLY.CometChatCalls.init((CallAppSettingBuilder()..appId = APP_ID..region = REGION).build(), onSuccess: ..., onError: ...) (named-callback form; CallAppSettingBuilder exposes only appId/region/host overrides — there is NO authKey field. Verified cometchat_calls_sdk-5.0.2 src/builder/call_app_settings_request.dart + src/plugin/cometchatcalls.dart:172). Permission requests. NO Chat SDK init.SessionStatusListeners. CometChatCalls.joinSession(sessionId:, sessionSettings: SessionSettingsBuilder().build(), onSuccess:, onError:). Renders the returned Widget? via SizedBox.expand. See references/call-session.md.BlocConsumer for auto-pop on idle transition.Why no Chat SDK / no UIKit: session mode never touches a Chat SDK call entity. No ringing, no UIKit incoming-call overlay needed.
Dual-SDK + UIKit. Scaffold:
flutter_callkit_incoming + Firebase setup.flutter_callkit_incoming bridge (you write this; the kit ships no VoIP/ring-UI module).CometChatCalls.joinSession(sessionId:, sessionSettings:, onSuccess:, onError:) with Widget rendered via SizedBox.expand. Cubit-driven state./calls route.navigatorKey: CallNavigationContext.navigatorKey wired; enableCalls = true on UIKitSettingsBuilder shows the foreground overlay automatically (rule 1.7). No widget mount needed.When chat is already integrated. The skill:
cometchat_chat_uikit is on ^6.0.1 — calls are already bundled.CometChatUIKit.init call to add CometChatUIKitCalls.init in the success callback (rule 1.1)...enableCalls = true is set on UIKitSettingsBuilder (rule 1.0) so foreground overlay fires automatically; ensures navigatorKey: CallNavigationContext.navigatorKey is on MaterialApp (rule 1.7).CometChatMessageHeader shows call buttons by default (hideVoiceCallButton: false, hideVideoCallButton: false).CometChatCallLogs as a tab/screen.CometChatUIKitCalls.init must run inside CometChatUIKit.init's onSuccess. Rule 1.1.CometChatIncomingCall(call: c, user: u) mounted on the messages screen only fires there. Mount the overlay at app root via MaterialApp.builder (rule 1.7).CometChatCallButtons. Group calls require group:; the widget silently mismatches if both are passed.didChangeDependencies(). Listed in V6 components catalog as a perf rule but applies to every call surface.cometchat_calls_uikit namespace and V6's cometchat_chat_uikit/cometchat_calls_uikit.dart barrel re-export different classes with the same name. Pick a cohort and stay there.Static:
cometchat_chat_uikit ^6.0.1 in pubspec.yaml (V6 bundles calls — no separate calls package)CometChatUIKitCalls.init called inside CometChatUIKit.init's onSuccess (rule 1.1)CometChatUIKitCalls.init called exactly once per app lifecycle..enableCalls = true on UIKitSettingsBuilder + navigatorKey: CallNavigationContext.navigatorKey on MaterialApp (rule 1.7) — no manual overlay widget mountCometChatDisplayIncomingCallOverlay (fictional class — does NOT exist; ENG-35698)Info.plist: NSCameraUsageDescription + NSMicrophoneUsageDescription + UIBackgroundModes (audio + voip + remote-notification)minSdk = 26, ProGuard rules -keep class com.cometchat.** { *; }endSession + FlutterCallkitIncoming.endAllCalls + Navigator popCallOperationsServiceLocator.instance.reset()resizeToAvoidBottomInset: false on call-screen ScaffoldsdidChangeDependencies(), not build()flutter_callkit_incoming + firebase_messaging + iOS PushKit platform-channel bridgeRuntime (real devices, both platforms):
references/advanced-features.md — Recording, Virtual Background, Audio Modes, Picture-in-Picture, Screen-share (receive) — source-verified against cometchat_calls_sdk 5.0.2cometchat-calls — dispatchercometchat-flutter-v6-core — UIKitSettings, init/login order, init guard rulescometchat-flutter-v6-events — Bloc event streamscometchat-flutter-v6-features — feature catalog (calls is a base capability — features layer on top)cometchat-flutter-v6-production — server-minted tokens, ProGuard, environment configcometchat-flutter-v6-troubleshooting — pubspec resolution, Bloc errors, theme cache, build errorscometchat-flutter-v6-migration — V5 → V6 migration recipes (GetX → Bloc, calls package → bundled, theme API rewrite)startSession on Android can return null with a 5-second timeout and no error feedback — field-observed on Pixel 3, not confirmed in the SDK source; retry once before surfacing failure. (Audio vs video is driven by the Chat-SDK Call.type string — there is no SessionType enum in the calls SDK.)Call(type: "audio") and confirm the rendered surface.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.