cometchat-flutter-v5-calls — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited cometchat-flutter-v5-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 v5 (GetX-based chat kit) on the v5 Calls SDK (cometchat_calls_sdk ^5.0.2, added as a direct dependency). Loaded by cometchat-calls when framework === "flutter" and flutter_version === "v5". Operates in two modes:
cometchat_chat_sdk (signaling) + cometchat_calls_sdk ^5.0.2 (WebRTC) without the cometchat_chat_uikit UI Kit. Custom call screens. VoIP push is mandatory — same rule as native iOS / Android.cometchat_chat_uikit), add the raw `cometchat_calls_sdk ^5.0.2` directly, and build a custom call surface around it. Mount the global incoming-call listener at the app shell.DEPENDENCY CONTEXT — why raw SDK, not the calls UI Kit (read first). The V5 Flutter calls UI Kitcometchat_calls_uikit(latest 5.0.16) transitively pinscometchat_calls_sdk ^4.2.2, and its prebuilt call widgets (CometChatCallButtons/CometChatIncomingCall/CometChatOutgoingCall/CometChatOngoingCall) are compiled against the 4.x API. To run the 5.x calls SDK (cometchat_calls_sdk ^5.0.2) on the Flutter V5 (GetX) chat UI Kit — per the product decision that all families use the V5 calls SDK — you integrate the raw `cometchat_calls_sdk ^5.0.2` directly with a custom call surface. You do NOT usecometchat_calls_uikit's prebuilt call widgets — they require the 4.x SDK and will clash with the 5.x pin. Wherever this skill mentions those widgets, they are a legacy 4.x-kit alternative, incompatible with the 5.x calls SDK. Substrate of record (Dart 5.x):~/.pub-cache/hosted/pub.dev/cometchat_calls_sdk-5.0.2/lib/.
Read these other skills first:
cometchat-calls — dispatcher (modes, hard rules, anti-patterns)cometchat-flutter-v5-core — UIKitSettingsBuilder, init/login order, GetX scope rules, app entry conventionscometchat-flutter-v5-events — CometChatMessageEvents / CometChatCallEvents subscription patternsGround truth:
~/.pub-cache/hosted/pub.dev/cometchat_calls_sdk-5.0.2/lib/ (lifecycle: src/plugin/cometchatcalls.dart; in-call controls: src/call_session.dart; the 5 split listeners: src/listener/*; settings: src/builder/session_settings.dart)THIS SKILL TARGETS THE 5.x CALLS SDK (read this first). Addcometchat_calls_sdk ^5.0.2as a direct dependency. The 5.x SDK splits its surface: lifecycle is static on `CometChatCalls` (init/login/loginWithAuthToken/logout/generateToken/generateCallToken/joinSession/getLoggedInUser) while in-call controls are instance methods on the `CallSession` singleton fetched viaCallSession.getInstance(). Settings useSessionSettingsBuilder(NOTCallSettingsBuilder—@Deprecatedin 5.0.2,src/builder/call_settings.dart:209). Events arrive through the 5 split listeners (NOT the singleCometChatCallsEventsListener—@Deprecated,src/listener/cometchat_calls_events_listener.dart:16). Do not pull calls transitively viacometchat_calls_uikit— that brings the 4.x SDK (see the dependency-context note above).
Unlike 4.x, the 5.x Calls SDK is logged in explicitly and caches the auth token internally — generateCallToken/joinSession then need no per-call token argument. The lifecycle (all static on CometChatCalls, src/plugin/cometchatcalls.dart):
init(CallAppSettings, {onSuccess, onError}) — :172login({uid:, authKey:, onSuccess:, onError:}) — :224loginWithAuthToken({authToken:, onSuccess:, onError:}) — :316 (production path)logout({onSuccess:, onError:}) — :409generateToken(sessionId, userAuthToken, {onSuccess, onError}) — :449 (@Deprecated v4-compat shim; prefer generateCallToken)generateCallToken(sessionId, {onSuccess, onError}) — :565 (uses the cached auth token)joinSession({sessionId: OR callToken:, sessionSettings:, onSuccess:, onError:}) — :735getLoggedInUser() — :698import 'package:cometchat_sdk/cometchat_sdk.dart';
import 'package:cometchat_calls_sdk/cometchat_calls_sdk.dart';
// 1. Chat SDK login (signaling).
CometChat.login(uid, AUTH_KEY,
onSuccess: (User user) async {
// 2. Calls SDK login (5.x has its own). Borrow the Chat auth token.
// getAuthToken() is NOT a User method — use the static CometChat.getUserAuthToken().
final authToken = await CometChat.getUserAuthToken();
if (authToken != null) {
CometChatCalls.loginWithAuthToken( // :316
authToken: authToken,
onSuccess: (cu) { /* Calls SDK ready */ },
onError: (CometChatCallsException e) { debugPrint(e.message ?? ''); },
);
}
},
onError: (CometChatException e) { /* chat login failed */ },
);You can also CometChatCalls.login(uid: uid, authKey: AUTH_KEY, ...) (:224) directly with the app's auth key in dev. In production use loginWithAuthToken with a server-minted token (rule 1.4).
Surprises:
CometChatCalls.init(...) (:172) takes a CallAppSettings (built via CallAppSettingBuilder, src/builder/call_app_settings_request.dart:36) — appId + region, no authKey field.CometChatCalls.getLoggedInUser() (:698) returns the calls user; generateCallToken (:565) and joinSession (:735) use the cached auth token — no per-call token arg.Both modes integrate the raw 5.x calls SDK directly (no calling-extension widget — that's a 4.x-kit construct). Chat SDK initiates ringing; the 5.x Calls SDK joins the WebRTC session via joinSession:
// ✓ RIGHT — raw 5.x dual-SDK (additive AND standalone)
import 'package:cometchat_chat_sdk/cometchat_chat_sdk.dart';
import 'package:cometchat_calls_sdk/cometchat_calls_sdk.dart';
// Chat SDK — initiate ringing
// Chat SDK Call — NAMED constructor; `type`/`receiverType` are Strings (the constants resolve to strings).
final outgoing = Call(
receiverUid: receiverUid,
type: CometChatCallType.video, // 'video'
receiverType: CometChatReceiverType.user, // 'user'
);
final settings = (SessionSettingsBuilder()
..setTitle('CometChat Call') // src/builder/session_settings.dart:155
..startAudioMuted(false) // :173
..startVideoPaused(false)) // :167
.build();
// initiateCall is VOID + callback-based — the Call (with sessionId) arrives in onSuccess,
// NOT via `await`/a return value. Join the WebRTC session inside that callback.
CometChat.initiateCall(
outgoing, // POSITIONAL Call arg
onSuccess: (Call initiated) {
CometChatCalls.joinSession(
sessionId: initiated.sessionId, // SDK mints the call token internally
sessionSettings: settings,
onSuccess: (Widget? widget) {
// render widget via SizedBox.expand; register split listeners on
// CallSession.getInstance() (rule 1.0 / references/call-session.md)
},
onError: (CometChatCallsException err) {
debugPrint('joinSession failed: ${err.message}');
},
);
},
onError: (CometChatException e) { debugPrint('initiateCall failed: ${e.message}'); },
);Legacy 4.x-kit alternative (incompatible with the 5.x calls SDK). The oldcometchat_calls_uikitpath registered aCometChatCallingExtensiononUIKitSettingsBuilderand usedCometChatUIKitCalls.startSession+ the prebuiltCometChatCallButtons/CometChatIncomingCall/CometChatOutgoingCall/CometChatOngoingCallwidgets. Those widgets are compiled against the 4.x calls SDK — they cannot run againstcometchat_calls_sdk ^5.0.2. Do not mix them in.
flutter_callkit_incoming + firebase_messaging + platform-channel bridgesStandalone mode requires working VoIP push end-to-end. The Flutter stack:
high, data payload — NOT notification).ios/Runner/AppDelegate.swift registering PKPushRegistry.voIP and forwarding payloads through a MethodChannel to Dart.In additive mode, this is opt-in but recommended.
⚠️ Android build prerequisite — Jetifier. If your build pulls in any legacy com.android.support:* AAR transitively, AGP fails assembleDebug with dozens of Duplicate class android.support.v4.* errors against androidx.core. Set in android/gradle.properties:
android.useAndroidX=true
android.enableJetifier=trueThe 5.x calls SDK ships CometChatOngoingCallService (src/services/ongoing_call_service.dart) — call CometChatOngoingCallService.launch() after the session starts and .abort() on teardown (both static Future<void>, no-arg). The host app's android/app/src/main/AndroidManifest.xml must declare the four FOREGROUND_SERVICE permissions plus MANAGE_OWN_CALLS / BIND_TELECOM_CONNECTION_SERVICE — the same rule as native Android (cf. cometchat-android-v5-calls rule 1.3).
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_PHONE_CALL" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA" />
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
<uses-permission android:name="android.permission.BIND_TELECOM_CONNECTION_SERVICE"
tools:ignore="ProtectedPermissions" />Silent crash on Android 14+ if FOREGROUND_SERVICE_PHONE_CALL is missing. The tools namespace must be declared on the <manifest> element.
cometchat-flutter-v5-production covers the token-endpoint pattern. In production, log BOTH SDKs in with a server-minted auth token, not the app auth key: CometChat.loginWithAuthToken(token) (Chat SDK) then CometChatCalls.loginWithAuthToken(authToken: token, onSuccess:, onError:) (Calls SDK, :316). The 5.x calls SDK caches that token internally — generateCallToken (:565) / joinSession (:735) then need no per-call token.
Future<void> endCall(String sessionId) async {
// 1. Leave the Calls SDK session — releases WebRTC tracks.
// In 5.x the canonical teardown is the CallSession INSTANCE method
// leaveSession() (call_session.dart:272). The static CometChatCalls.endSession
// still exists (cometchatcalls.dart:676) but is @Deprecated — it just
// delegates to CallSession.getInstance()?.leaveSession() internally.
await CallSession.getInstance()?.leaveSession();
await CometChatOngoingCallService.abort();
// 2. Tell the OS-level call UI the call ended
await FlutterCallkitIncoming.endAllCalls();
// 3. Pop the call screen
if (mounted) {
Navigator.of(context, rootNavigator: true).popUntil((r) => r.isFirst);
}
// 4. After logout flows: reset ServiceLocator if it was used directly
// (UIKit handles this for additive mode)
}Skipping FlutterCallkitIncoming.endAllCalls leaves the lock-screen/heads-up call UI stuck. Skipping the Navigator.popUntil strands the Dart-side call screen with WebRTC views still in the tree.
permission_handler + Info.plist + AndroidManifestimport 'package:permission_handler/permission_handler.dart';
await [Permission.camera, Permission.microphone, Permission.notification].request();iOS — ios/Runner/Info.plist:
<key>NSCameraUsageDescription</key>
<string>So you can be seen during video calls.</string>
<key>NSMicrophoneUsageDescription</key>
<string>So you can talk during voice and video calls.</string>
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
<string>voip</string>
<string>remote-notification</string>
</array>Android — manifest as above (rule 1.3) plus runtime requests via permission_handler.
With the raw 5.x SDK + custom call surface, use a plain GlobalKey<NavigatorState> on MaterialApp.navigatorKey so call overlays can navigate even when a call is initiated from a sub-route. (Do NOT use CallNavigationContext.navigatorKey — that is a cometchat_calls_uikit (4.x-kit) symbol and is not available on the raw 5.x SDK path.)
// ✓ RIGHT
final navigatorKey = GlobalKey<NavigatorState>();
MaterialApp(
navigatorKey: navigatorKey,
// ...
);Incoming calls are signaled by the Chat SDK CallListener — register it app-wide in the app shell's State, not in a feature screen:
class AppShellState extends State<AppShell> with CallListener {
static const _listenerId = 'app-shell-call-listener';
@override
void initState() {
super.initState();
CometChat.addCallListener(_listenerId, this);
}
@override
void dispose() {
CometChat.removeCallListener(_listenerId);
super.dispose();
}
@override
void onIncomingCallReceived(Call call) {
// Show your custom incoming-call UI / flutter_callkit_incoming, then on
// accept: CometChat.acceptCall(call.sessionId) → CometChatCalls.joinSession(...)
}
}Use a stable string ID for the listener — duplicate IDs overwrite, distinct IDs fire both (double-ring bug).
# pubspec.yaml
dependencies:
flutter:
sdk: flutter
cometchat_chat_uikit: ^5.2.14 # additive mode — existing GetX chat kit (keep)
cometchat_calls_sdk: ^5.0.2 # DIRECT dependency — the 5.x calls SDK (raw)
permission_handler: ^11.0.0 # rule 1.6
flutter_callkit_incoming: ^2.0.0 # standalone — VoIP UI bridge
firebase_messaging: ^14.0.0 # standalone — Android FCM
firebase_core: ^2.0.0 # firebase_messaging peerDo NOT add `cometchat_calls_uikit`. Its latest (5.0.16) transitively pinscometchat_calls_sdk ^4.2.2and clashes with the 5.x SDK above. The rawcometchat_calls_sdk ^5.0.2is the calls dependency; build a custom call surface around it (Section 3). See the dependency-context note at the top.
Hosted source if pub.dev resolution lags:
cometchat_calls_sdk:
hosted: https://dart.cloudsmith.io/cometchat/cometchat/
version: ^5.0.2Init order — init both SDKs, then log both in (rule 1.0):
import 'package:cometchat_chat_uikit/cometchat_chat_uikit.dart'; // additive chat kit
import 'package:cometchat_calls_sdk/cometchat_calls_sdk.dart';
// Chat UI Kit init (additive — existing GetX chat integration)
final settings = (UIKitSettingsBuilder()
..appId = CometChatConfig.appId
..region = CometChatConfig.region
..authKey = CometChatConfig.authKey
..subscriptionType = CometChatSubscriptionType.allUsers)
.build();
await CometChatUIKit.init(uiKitSettings: settings);
// 5.x Calls SDK init (raw) — CallAppSettingBuilder, no authKey field
final callAppSettings = (CallAppSettingBuilder()
..appId = CometChatConfig.appId
..region = CometChatConfig.region)
.build();
CometChatCalls.init( // cometchatcalls.dart:172
callAppSettings,
onSuccess: (String success) { /* Calls SDK ready */ },
onError: (CometChatCallsException e) { /* surface to user */ },
);
// After login, also CometChatCalls.loginWithAuthToken(...) — rule 1.0.Standalone mode is identical except you use the raw cometchat_chat_sdk (CometChat.init) instead of the chat UI Kit for signaling.
Import the raw SDK barrel — it re-exports the lifecycle class, CallSession, SessionSettingsBuilder, the 5 listeners, the enums, and CometChatOngoingCallService:
import 'package:cometchat_calls_sdk/cometchat_calls_sdk.dart';You build the call surface yourself (custom widgets) around these APIs. There are no prebuilt 5.x call widgets in this package — joinSession hands back a Widget? you render via SizedBox.expand, and you wrap it with your own controls bound to the CallSession singleton.
Lifecycle — static on `CometChatCalls` (src/plugin/cometchatcalls.dart):
| Method | Line | Purpose |
|---|---|---|
init(CallAppSettings, {onSuccess, onError}) | :172 | Initialize the SDK (appId + region) |
login({uid:, authKey:, onSuccess:, onError:}) | :224 | Dev login with app auth key |
loginWithAuthToken({authToken:, onSuccess:, onError:}) | :316 | Production login (server-minted token) |
logout({onSuccess:, onError:}) | :409 | Log out, clear cached token |
generateCallToken(sessionId, {onSuccess, onError}) | :565 | Mint a CallToken using the cached auth token |
generateToken(sessionId, userAuthToken, {...}) | :449 | @Deprecated v4-compat shim (explicit token) |
joinSession({sessionId: OR callToken:, sessionSettings:, onSuccess:, onError:}) | :735 | Join WebRTC; onSuccess → Widget? |
getLoggedInUser() | :698 | The logged-in calls user |
endSession({onSuccess:, onError:}) | :676 | @Deprecated — delegates to CallSession.getInstance()?.leaveSession() |
In-call controls — instance on `CallSession.getInstance()` (src/call_session.dart):
| Method | Line | Purpose |
|---|---|---|
muteAudio() / unMuteAudio() | :131 / :145 | Local audio mute (no-arg) |
pauseVideo() / resumeVideo() | :168 / :182 | Local video pause (no-arg) |
switchCamera() | :205 | Flip front/back camera |
raiseHand() / lowerHand() | :292 / :306 | Hand raise |
setLayout(LayoutType) | :394 | tile / sidebar / spotlight |
setAudioModeType(AudioMode) | :381 | speaker / earpiece / bluetooth |
startRecording() / stopRecording() | :451 / :465 | Recording |
enablePictureInPictureLayout() / disablePictureInPictureLayout() | :223 / :236 | PiP layout toggle |
enterPipMode() / isPipSupported() | :250 / :263 | OS-level PiP (Future<bool>) |
openVirtualBackgroundSettingsPanel() | :438 | Virtual Background — the raw 5.0.2 SDK DOES support VB (unlike the 4.x calls-uikit) |
muteParticipant(String) / pauseParticipantVideo(String) | :329 / :342 | Moderator: mute/pause a remote participant |
pinParticipant(String?) / unPinParticipant() | :355 / :368 | Pin/unpin a participant (Flutter: single nullable arg + unPinParticipant capital P — differs from Android) |
setChatButtonUnreadCount(int) | :489 | In-call chat badge |
leaveSession() | :272 | End session, release tracks |
Settings: SessionSettingsBuilder (src/builder/session_settings.dart) — setTitle (:155), startAudioMuted(bool) (:173), startVideoPaused(bool) (:167), setLayout(LayoutType) (:179), setIdleTimeoutPeriod(int seconds) (:185), setAudioMode(AudioMode) (:191), and the hide*Button(bool) family (:215–:305). NOT `CallSettingsBuilder` (@Deprecated, src/builder/call_settings.dart:209).
Listeners — register on `CallSession.getInstance()`: the 5 split interfaces in src/listener/: SessionStatusListeners, ParticipantEventListeners, MediaEventListeners, ButtonClickListeners, LayoutListeners. NOT the single `CometChatCallsEventsListener` (@Deprecated, src/listener/cometchat_calls_events_listener.dart:16). See references/call-session.md.
Legacy 4.x-kit alternative (incompatible with the 5.x calls SDK).cometchat_calls_uikitships prebuiltCometChatCallButtons/CometChatIncomingCall/CometChatOutgoingCall/CometChatOngoingCall/CometChatCallLogswidgets and aCometChatUIKitCallsfacade — but those are compiled against the 4.x calls SDK (cometchat_calls_sdk ^4.2.2). They cannot be used withcometchat_calls_sdk ^5.0.2. Replicate their behavior with your own widgets bound to the raw 5.x API above. For call-log history, fetch viaCometChatCalls.getCallDetails(sessionId, ...)or the Chat SDKCallLogRequestBuilderand render your own list.
When product === "voice-video" and there is no v5 chat integration.
Split by calling mode:
5.x Calls SDK ONLY. The Chat SDK is present only as the source of the auth token used to log the Calls SDK in (rule 1.0). Scaffold:
cometchat_calls_sdk: ^5.0.2 + cometchat_chat_sdk (for the auth token). No cometchat_chat_uikit, no cometchat_calls_uikit.CometChat.init(...) + CometChat.login(...), then CometChatCalls.init(callAppSettings, onSuccess:, onError:) (build via CallAppSettingBuilder()..appId=…..region=…) + CometChatCalls.loginWithAuthToken(authToken: ..., ...) + permission_handler flow.StatefulWidget implementing the relevant 5.x split listeners (SessionStatusListeners, etc.). CometChatCalls.joinSession(sessionId: ..., sessionSettings: SessionSettingsBuilder().build(), onSuccess:, onError:). Register listeners on CallSession.getInstance() in onSuccess. CometChatOngoingCallService.launch/abort. See references/call-session.md.Why no chat UI Kit / no VoIP push: session mode never touches a Chat SDK call entity. No ringing. The Chat SDK is present only to mint the auth token for CometChatCalls.loginWithAuthToken.
Dual-SDK: Chat SDK signaling + 5.x Calls SDK media. Scaffold:
flutter_callkit_incoming events + firebase_messaging + iOS platform-channel PushKit. On payload → FlutterCallkitIncoming.showCallkitIncoming(...). On accept → CometChat.acceptCall(sessionId) → navigate to ongoing-call screen.CometChat.initiateCall(...).CometChatCalls.joinSession(sessionId: ..., sessionSettings: ..., onSuccess:, onError:) with the returned Widget? rendered via SizedBox.expand. Implements rule 1.5 cleanup (CallSession.getInstance()?.leaveSession()). Sets resizeToAvoidBottomInset: false./calls route. Custom list via Chat SDK CallLogRequestBuilder (or CometChatCalls.getCallDetails).GlobalKey<NavigatorState> (rule 1.7).Info.plist (rule 1.6), AndroidManifest.xml (rule 1.3 + FCM service registration), Firebase config (google-services.json in android/app/, GoogleService-Info.plist in ios/Runner/).When chat is already integrated (existing GetX cometchat_chat_uikit). The skill:
pubspec.yaml — NOT cometchat_calls_uikit.GlobalKey<NavigatorState> on MaterialApp.navigatorKey (rule 1.7).CallListener in the app-shell State (rule 1.7).CometChatMessageHeader's trailing slot) → CometChat.initiateCall(...).CometChatCalls.joinSession + CallSession controls (Section 3, references/call-session.md).cometchat_calls_sdk ^4.2.2 and clashes with the 5.x SDK; its prebuilt call widgets are 4.x-bound. Use the raw 5.x SDK + custom surface.@Deprecated in 5.0.2). Use SessionSettingsBuilder, the 5 split listeners, and joinSession.CometChatCalls.muteAudio(...), CometChatCalls.setLayout(...)). In 5.x those are INSTANCE methods on CallSession.getInstance().data. ConnectionService can't intercept notification payloads. Server must send data: { type: "incoming_call", sessionId: ... } with priority: "high".Static:
cometchat_calls_sdk ^5.0.2 is a DIRECT dependency in pubspec.yaml; cometchat_calls_uikit is ABSENTCallSettingsBuilder / CometChatCallsEventsListener / CometChatCalls.startSession / CometChatUIKitCalls / CometChatCallingExtension anywhere (all 4.x)CometChatCalls.init/loginWithAuthToken/joinSession; settings via SessionSettingsBuilder; events via the 5 split listeners on CallSession.getInstance()CallSession.getInstance()?.X(), not static CometChatCalls.X()MaterialApp.navigatorKey is a GlobalKey<NavigatorState>CallListener attached in app-shell State, removed in disposepermission_handlerInfo.plist: NSCameraUsageDescription + NSMicrophoneUsageDescription + UIBackgroundModes (audio + voip + remote-notification)CallSession.getInstance()?.leaveSession() + CometChatOngoingCallService.abort() + FlutterCallkitIncoming.endAllCalls() + Navigator.popUntil (rule 1.5)google-services.json + GoogleService-Info.plist)flutter_callkit_incoming + firebase_messaging + iOS PushKit platform-channel bridgeRuntime (real devices, both platforms):
cometchat-calls — dispatchercometchat-flutter-v5-core — UIKitSettingsBuilder, init/login order, GetX scopecometchat-flutter-v5-events — CometChatCallEvents subscription patternscometchat-flutter-v5-push — FCM/APNs for chat (overlap with VoIP push but distinct paths)cometchat-flutter-v5-production — server-minted tokens, ProGuard, environment configcometchat-flutter-v5-troubleshooting — pubspec conflicts, GetX issues, Pod errors, runtime crashescometchat-flutter-v6-calls + cometchat-flutter-v6-migration — when migrating to V6 (calls fold into the unified package, Bloc replaces GetX)~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.