CometChat Flutter UIKit — Calls
Voice and video calling for 1:1 and group conversations. Call functionality is built into cometchat_chat_uikit — no separate package needed.
Import
import 'package:cometchat_chat_uikit/cometchat_chat_uikit.dart';
import 'package:cometchat_chat_uikit/cometchat_calls_uikit.dart';
The first import gives you chat components. The second gives you call-specific types.
Rule: CALLS_INIT_AFTER_CHAT_INIT
CometChatUIKitCalls.init() MUST be called after CometChatUIKit.init() completes. The calls SDK needs the chat SDK's auth context.
// ✅ CORRECT — calls init after chat init
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
CometChatUIKit.init(uiKitSettings: settings);
CometChatUIKitCalls.init(appId, region); // Race condition
Rule: SINGLE_CALLS_INIT
CometChatUIKitCalls.init() must be called exactly once per app lifecycle. Calling it again after logout + re-login can cause "session already started" errors.
Rule: CALLS_REINIT_AFTER_LOGOUT
After CometChatUIKit.logout(), the calls SDK session is invalidated. On the next login, CometChatUIKitCalls.init() must be called again.
Architecture
call_ui/src/
├── call_buttons/ # Voice/video call buttons (CallButtonsBloc)
├── incoming_call/ # Incoming call screen (IncomingCallBloc)
├── outgoing_call/ # Outgoing call screen (OutgoingCallBloc)
├── ongoing_call/ # Active call screen — WebRTC (OngoingCallBloc)
├── call_logs/ # Call history list (CallLogsBloc + Clean Arch)
├── call_operations/ # Shared clean architecture (DI, use cases, repos)
├── call_bubble/ # Call message bubble in chat
├── call_settings/ # CometChatUIKitCalls, CallNavigationContext
├── native_call_kit/ # iOS CallKit / Android ConnectionService
├── utils/ # CallUtils, CallStateService, CallPermissions
├── call_event_service.dart # Centralized call event handling
└── calling_configuration.dart # Top-level config object
Components
CometChatCallButtons
Voice and video call buttons. Typically placed in CometChatMessageHeader's trailing view or standalone.
CometChatCallButtons(
user: user, // For 1:1 calls
group: group, // For group calls (meetings)
hideVoiceCallButton: false,
hideVideoCallButton: false,
voiceCallIcon: Icon(Icons.call),
videoCallIcon: Icon(Icons.videocam),
callButtonsStyle: CometChatCallButtonsStyle(
voiceCallIconColor: colorPalette.iconPrimary,
videoCallIconColor: colorPalette.iconPrimary,
),
outgoingCallConfiguration: CometChatOutgoingCallConfiguration(
outgoingCallStyle: CometChatOutgoingCallStyle(...),
),
)
Call buttons are built into CometChatMessageHeader by default:
CometChatMessageHeader(
user: user,
group: group,
hideVoiceCallButton: false,
hideVideoCallButton: false,
)
CometChatIncomingCall
Displays when the logged-in user receives a call. Supports accept/decline with custom views.
CometChatIncomingCall(
call: call, // Required — the active Call object
user: callerUser, // Caller info for display
onAccept: (ctx, call) { },
onDecline: (ctx, call) { },
disableSoundForCalls: false,
customSoundForCalls: 'assets/ringtone.mp3',
incomingCallStyle: CometChatIncomingCallStyle(
backgroundColor: colorPalette.background3,
acceptButtonColor: colorPalette.success,
declineButtonColor: colorPalette.error,
),
// Custom view slots
titleView: (ctx, call) => Text('Custom Title'),
subTitleView: (ctx, call) => Text('Custom Subtitle'),
leadingView: (ctx, call) => Icon(Icons.call),
trailingView: (ctx, call) => CometChatAvatar(image: user.avatar),
)
CometChatOutgoingCall
Displays when the logged-in user initiates a call.
CometChatOutgoingCall(
call: call,
user: receiverUser,
outgoingCallStyle: CometChatOutgoingCallStyle(
backgroundColor: colorPalette.background1,
),
)
CometChatOngoingCall
The active call screen with WebRTC video/audio. Rendered after a call is accepted.
CometChatOngoingCall(
callSettingsBuilder: sessionSettingsBuilder,
sessionId: call.sessionId,
callWorkFlow: CallWorkFlow.directCalling,
)
CometChatCallLogs
Scrollable list of call history. Clean Architecture + BLoC with CallLogsServiceLocator.
CometChatCallLogs(
onItemClick: (callLog) { /* Navigate or initiate call */ },
callLogsStyle: CallLogsStyle(backgroundColor: colorPalette.background1),
)
CometChatCallBubble
Message bubble for call events in the message list. Automatically rendered for call-type messages.
CallingConfiguration
Top-level configuration object bundling all call component configs:
CallingConfiguration(
outgoingCallConfiguration: CometChatOutgoingCallConfiguration(...),
incomingCallConfiguration: CometChatIncomingCallConfiguration(...),
callButtonsConfiguration: CallButtonsConfiguration(...),
groupSessionSettingsBuilder: sessionSettingsBuilder,
)
CometChatUIKitCalls API
| Method | Purpose |
|---|---|
CometChatUIKitCalls.init(appId, region) |
Initialize calls SDK |
CometChatUIKitCalls.initiateCall(call) |
Start a call |
CometChatUIKitCalls.acceptCall(sessionId) |
Accept incoming call |
CometChatUIKitCalls.rejectCall(sessionId, status) |
Reject/cancel call |
CometChatUIKitCalls.generateToken(sessionId) |
Generate call token |
CometChatUIKitCalls.startSession(sessionId, settings) |
Start WebRTC session |
CometChatUIKitCalls.endSession() |
End active call session |
Call Flow — 1:1 Direct Call
Caller Receiver
|-- CometChatCallButtons tap ------->|
|-- initiateCall(Call) ------------->|
|-- CometChatOutgoingCall shown ---->|
| |-- CometChatIncomingCall shown
| |-- acceptCall(sessionId)
|<-- onOutgoingCallAccepted ---------|
|-- generateToken(sessionId) ------->|
|-- startSession(token, settings) -->|
|-- CometChatOngoingCall shown ----->|-- CometChatOngoingCall shown
|-- endSession() ------------------>|-- endSession()
Golden Path — Messages Screen with Calls
Scaffold(
resizeToAvoidBottomInset: false,
appBar: CometChatMessageHeader(
user: user,
group: group,
onBack: () => Navigator.pop(context),
hideVoiceCallButton: false,
hideVideoCallButton: false,
),
body: Column(
children: [
Expanded(child: CometChatMessageList(user: user, group: group)),
CometChatMessageComposer(user: user, group: group),
],
),
)
BLoC Events
CallButtonsBloc
| Event | Purpose |
|---|---|
InitiateVoiceCall |
Start audio call |
InitiateVideoCall |
Start video call |
IncomingCallBloc
| Event | Purpose |
|---|---|
AcceptCall |
Accept the incoming call |
RejectCall |
Decline the incoming call |
OutgoingCallBloc
| Event | Purpose |
|---|---|
CancelCall |
Cancel the outgoing call |
CallAccepted(call) |
Receiver accepted |
CallRejected(call) |
Receiver rejected |
OngoingCallBloc
| Event | Purpose |
|---|---|
StartSession(sessionId, settings) |
Begin WebRTC session |
EndSession |
End the call |
CallLogsBloc
| Event | Purpose |
|---|---|
LoadCallLogs |
Initial load |
LoadMoreCallLogs |
Pagination |
Gotchas
CometChatUIKitCalls.init()must happen AFTERCometChatUIKit.init()— parallel init causes "auth token null".- After logout, the calls SDK session is invalidated. Re-init required on next login.
startSessionon Android can returnnullwith a 5-second timeout and no error feedback. Known platform issue.SessionType.audiois ignored on Android in some SDK versions — always opens with video. Known external SDK bug.- Group calls use
CometChat.GroupnotCometChat.UseronCometChatCallButtons. - Incoming call handling must be global (mounted at app root level) so calls ring on every screen.
CallOperationsServiceLocatormust be initialized before using call BLoCs directly. UIKit widgets handle this automatically.- After logout, call
CallOperationsServiceLocator.instance.reset()to clean up. - On iOS, CallKit integration via
native_call_kitshows the native call UI. EnsureInfo.plisthas thevoipbackground mode. - On Android, the ongoing call foreground service shows a notification during active calls. Launched automatically by
startSession.
Anti-Patterns
// ❌ WRONG — init calls SDK before chat SDK
CometChatUIKitCalls.init(appId, region);
CometChatUIKit.init(uiKitSettings: settings);
// ✅ CORRECT — calls init in chat init's onSuccess
CometChatUIKit.init(
uiKitSettings: settings,
onSuccess: (_) {
CometChatUIKitCalls.init(appId, region);
},
);
// ❌ WRONG — not handling incoming calls globally
CometChatIncomingCall(call: call, user: user) // Only on messages screen
// ✅ CORRECT — mount incoming call overlay at app root
MaterialApp(
builder: (context, child) {
return Stack(children: [child!, /* Global incoming call listener */]);
},
)
Checklist
-
CometChatUIKitCalls.init()called afterCometChatUIKit.init()completes - Incoming call handling is global (app root level), not per-screen
- Camera + microphone permissions handled (automatic via
CallPermissions) -
resizeToAvoidBottomInset: falseon any Scaffold with call UI - Call buttons visible in
CometChatMessageHeader(default) or standalone - After logout, calls SDK re-initialized on next login
- Android:
minSdk = 26, ProGuard rules forcom.cometchat.** - iOS:
voipbackground mode inInfo.plistfor CallKit - Theme cached in
didChangeDependencies(), notbuild() - SDK listeners cleaned up in
dispose()