Skip to main content
This guide covers migrating from Calls SDK v4 to v5 for Flutter.

Drop-in Compatibility

Calls SDK v5 is a drop-in replacement for v4. All v4 APIs are preserved as deprecated methods that internally delegate to the new v5 implementations. You can update the package version and your existing code will compile and run without changes.
dependencies:
  cometchat_calls_sdk:
    hosted: https://dart.cloudsmith.io/cometchat/cometchat/
    version: ^5.0.0
If you’re using CometChat UI Kits, simply updating the Calls SDK version is sufficient. The UI Kit will continue to work with v5 through the deprecated compatibility layer.

Why Migrate to v5 APIs?

While v4 APIs will continue to work, migrating to v5 APIs gives you:
  • Granular event listeners — 5 focused listener classes (SessionStatusListener, ParticipantEventListener, MediaEventsListener, ButtonClickListener, LayoutListener) instead of one monolithic CometChatCallsEventsListener
  • CallSession singleton for cleaner session control — all actions on a single instance instead of scattered static methods
  • Dedicated login() method — the Calls SDK now handles its own authentication instead of depending on the Chat SDK’s auth token or REST APIs
  • Strongly-typed enumsSessionType, LayoutType, AudioMode, CameraFacing instead of raw strings

Initialization

No changes required. The init() API is the same in v5.
CallAppSettings callAppSettings = (CallAppSettingBuilder()
  ..appId = "APP_ID"
  ..region = "REGION"
).build();

CometChatCalls.init(
  callAppSettings,
  onSuccess: (String message) {
    // Initialized
  },
  onError: (CometChatCallsException e) {
    // Handle error
  },
);

Authentication

In v4, the Calls SDK had no dedicated authentication step. It relied on the Chat SDK’s auth token (CometChat.getUserAuthToken()) or a REST API to obtain an auth token, which you then passed manually to generateToken(). v5 introduces its own login() method. After calling login(), the SDK caches the auth token internally, so you no longer need to pass it around to other API calls.
// No dedicated Calls SDK login — relied on Chat SDK for auth token
String authToken = CometChat.getUserAuthToken();

// Had to pass authToken manually
CometChatCalls.generateToken(sessionId, authToken,
  onSuccess: (GenerateToken token) {
    // Use token.token to start session
  },
  onError: (CometChatCallsException e) {},
);
Call CometChatCalls.loginWithAuthToken() once after your user authenticates (e.g., right after CometChat.login() succeeds). The SDK stores the auth token internally, so subsequent calls like generateToken() and joinSession() use it automatically without you having to pass it.

Session Settings

CallSettingsBuilder is replaced by SessionSettingsBuilder. The builder methods have been renamed for clarity and use strongly-typed enums.
CallSettings callSettings = (CallSettingsBuilder()
    ..isAudioOnly = true
    ..enableDefaultLayout = true
    ..showEndCallButton = true
    ..showMuteAudioButton = true
    ..showPauseVideoButton = true
    ..showSwitchCameraButton = true
    ..showRecordingButton = true
    ..startWithAudioMuted = false
    ..startWithVideoMuted = false
    ..defaultAudioMode = "SPEAKER"
    ..mode = "DEFAULT"
    ..startRecordingOnCallStart = false
    ..listener = myCallsEventsListener)
  .build();

Builder Method Mapping

v4 CallSettingsBuilderv5 SessionSettingsBuilderNotes
isAudioOnly = truesetType(SessionType.audio)Use SessionType.video for video calls
enableDefaultLayout = boolhideControlPanel(!bool) + hideHeaderPanel(!bool)Inverted logic
showEndCallButton = boolhideLeaveSessionButton(!bool)Inverted logic
showMuteAudioButton = boolhideToggleAudioButton(!bool)Inverted logic
showPauseVideoButton = boolhideToggleVideoButton(!bool)Inverted logic
showSwitchCameraButton = boolhideSwitchCameraButton(!bool)Inverted logic
showRecordingButton = boolhideRecordingButton(!bool)Inverted logic
showScreenSharingButton = boolhideScreenSharingButton(!bool)Inverted logic
showRaiseHandButton = boolhideRaiseHandButton(!bool)Inverted logic
showShareInviteButton = boolhideShareInviteButton(!bool)Inverted logic
showParticipantListButton = boolhideParticipantListButton(!bool)Inverted logic
showLayoutToggleButton = boolhideChangeLayoutButton(!bool)Inverted logic
startWithAudioMuted = boolstartAudioMuted(bool)Same logic
startWithVideoMuted = boolstartVideoPaused(bool)Same logic
defaultAudioMode = "SPEAKER"setAudioMode(AudioMode.speaker)Enum instead of string
mode = "DEFAULT"setLayout(LayoutType.tile)Enum instead of string
defaultCameraFacing = "FRONT"setInitialCameraFacing(CameraFacing.front)Enum instead of string
startRecordingOnCallStart = boolenableAutoStartRecording(bool)Same logic
lowBandwidthMode = boolenableLowBandwidthMode(bool)Same logic
idleTimeoutPeriod = intsetIdleTimeoutPeriod(int)Same logic
listener = listenerUse CallSession listenersSee Events section

Joining a Session

startSession() is replaced by joinSession(), which returns a Widget? that you place in your Flutter widget tree.
// Step 1: Generate token with auth token
CometChatCalls.generateToken(sessionId, authToken,
  onSuccess: (GenerateToken token) {
    // Step 2: Start session with token string
    CometChatCalls.startSession(token.token ?? "", callSettings,
      onSuccess: (Widget? callWidget) {
        // Place callWidget in your widget tree
      },
      onError: (CometChatCallsException e) {},
    );
  },
  onError: (CometChatCallsException e) {},
);
Key differences:
  • v5 generateToken() no longer requires the authToken parameter (uses cached token from login())
  • v5 joinSession() accepts a CallToken object or a sessionId string directly
  • v5 offers a convenience overload that takes sessionId directly and handles token generation internally

Session Control (Actions)

In v4, session actions were static methods on CometChatCalls. In v5, they’re instance methods on CallSession.
CometChatCalls.endSession(
  onSuccess: (String msg) {},
  onError: (CometChatCallsException e) {},
);
CometChatCalls.switchCamera();
CometChatCalls.muteAudio(true);
CometChatCalls.pauseVideo(true);
CometChatCalls.setAudioMode("SPEAKER");
CometChatCalls.enterPIPMode();
CometChatCalls.exitPIPMode();
CometChatCalls.startRecording();
CometChatCalls.stopRecording();

Action Method Mapping

v4 Static Methodv5 CallSession Method
CometChatCalls.endSession(...)callSession.leaveSession()
CometChatCalls.switchCamera()callSession.switchCamera()
CometChatCalls.muteAudio(true)callSession.muteAudio()
CometChatCalls.muteAudio(false)callSession.unMuteAudio()
CometChatCalls.pauseVideo(true)callSession.pauseVideo()
CometChatCalls.pauseVideo(false)callSession.resumeVideo()
CometChatCalls.setAudioMode(mode)callSession.setAudioModeType(AudioMode)
CometChatCalls.enterPIPMode()callSession.enablePictureInPictureLayout()
CometChatCalls.exitPIPMode()callSession.disablePictureInPictureLayout()
CometChatCalls.startRecording()callSession.startRecording()
CometChatCalls.stopRecording()callSession.stopRecording()
CometChatCalls.switchToVideoCall()Removed

Event Listeners

This is the biggest improvement in v5. The single CometChatCallsEventsListener mixin is replaced by 5 focused listener classes.

v4: Single Monolithic Listener

class MyCallController with CometChatCallsEventsListener {

  void setupCallEvents() {
    // Set via CallSettingsBuilder
    callSettingsBuilder..listener = this;
  }

  @override
  void onCallEnded() {}

  @override
  void onCallEndButtonPressed() {}

  @override
  void onSessionTimeout() {}

  @override
  void onUserJoined(RTCUser user) {}

  @override
  void onUserLeft(RTCUser user) {}

  @override
  void onUserListChanged(List<RTCUser> users) {}

  @override
  void onAudioModeChanged(List<AudioMode> devices) {}

  @override
  void onCallSwitchedToVideo(CallSwitchRequestInfo info) {}

  @override
  void onUserMuted(RTCMutedUser muteObj) {}

  @override
  void onRecordingToggled(RTCRecordingInfo info) {}

  @override
  void onError(CometChatCallsException e) {}
}

v5: Focused Listener Classes

CallSession? callSession = CallSession.getInstance();

// Session lifecycle events
final sessionStatusListener = SessionStatusListener(
  onSessionJoined: () {},
  onSessionLeft: () {},
  onSessionTimedOut: () {},
  onConnectionLost: () {},
  onConnectionRestored: () {},
  onConnectionClosed: () {},
);
callSession?.addSessionStatusListener(sessionStatusListener);

// Participant events (replaces onUserJoined, onUserLeft, onUserListChanged, onUserMuted)
final participantEventListener = ParticipantEventListener(
  onParticipantJoined: (Participant participant) {},
  onParticipantLeft: (Participant participant) {},
  onParticipantListChanged: (List<Participant> participants) {},
  onParticipantAudioMuted: (Participant participant) {},
  onParticipantAudioUnmuted: (Participant participant) {},
  onParticipantVideoPaused: (Participant participant) {},
  onParticipantVideoResumed: (Participant participant) {},
  onParticipantHandRaised: (Participant participant) {},
  onParticipantHandLowered: (Participant participant) {},
  onParticipantStartedRecording: (Participant participant) {},
  onParticipantStoppedRecording: (Participant participant) {},
  onDominantSpeakerChanged: (Participant participant) {},
);
callSession?.addParticipantEventListener(participantEventListener);

// Media state events (replaces onAudioModeChanged, onRecordingToggled)
final mediaEventsListener = MediaEventsListener(
  onAudioMuted: () {},
  onAudioUnMuted: () {},
  onVideoPaused: () {},
  onVideoResumed: () {},
  onRecordingStarted: () {},
  onRecordingStopped: () {},
  onAudioModeChanged: (AudioMode audioMode) {},
  onCameraFacingChanged: (CameraFacing facing) {},
);
callSession?.addMediaEventsListener(mediaEventsListener);

// Button click events (replaces onCallEndButtonPressed)
final buttonClickListener = ButtonClickListener(
  onLeaveSessionButtonClicked: () {},
  onToggleAudioButtonClicked: () {},
  onToggleVideoButtonClicked: () {},
  onSwitchCameraButtonClicked: () {},
  onRaiseHandButtonClicked: () {},
  onRecordingToggleButtonClicked: () {},
);
callSession?.addButtonClickListener(buttonClickListener);

// Layout events
final layoutListener = LayoutListener(
  onCallLayoutChanged: (LayoutType layoutType) {},
  onPictureInPictureLayoutEnabled: () {},
  onPictureInPictureLayoutDisabled: () {},
);
callSession?.addLayoutListener(layoutListener);
Flutter listeners are not lifecycle-aware. You must manually remove all listeners in your widget’s dispose() method to prevent memory leaks.
@override
void dispose() {
  CallSession.getInstance()?.removeSessionStatusListener(sessionStatusListener);
  CallSession.getInstance()?.removeParticipantEventListener(participantEventListener);
  CallSession.getInstance()?.removeMediaEventsListener(mediaEventsListener);
  CallSession.getInstance()?.removeButtonClickListener(buttonClickListener);
  CallSession.getInstance()?.removeLayoutListener(layoutListener);
  super.dispose();
}

Event Mapping

v4 Eventv5 Listenerv5 Event
onCallEnded()SessionStatusListeneronSessionLeft()
onCallEndButtonPressed()ButtonClickListeneronLeaveSessionButtonClicked()
onSessionTimeout()SessionStatusListeneronSessionTimedOut()
onUserJoined(RTCUser)ParticipantEventListeneronParticipantJoined(Participant)
onUserLeft(RTCUser)ParticipantEventListeneronParticipantLeft(Participant)
onUserListChanged(List<RTCUser>)ParticipantEventListeneronParticipantListChanged(List<Participant>)
onAudioModeChanged(List<AudioMode>)MediaEventsListeneronAudioModeChanged(AudioMode)
onCallSwitchedToVideo(CallSwitchRequestInfo)Removed
onUserMuted(RTCMutedUser)ParticipantEventListeneronParticipantAudioMuted(Participant)
onRecordingToggled(RTCRecordingInfo)MediaEventsListeneronRecordingStarted() / onRecordingStopped()
onError(CometChatCallsException)Errors returned via onError callbacks

New Events in v5

These events are only available with the v5 listener APIs:
ListenerEventDescription
SessionStatusListeneronConnectionLost()Network interrupted
SessionStatusListeneronConnectionRestored()Network restored
SessionStatusListeneronConnectionClosed()Connection permanently closed
ParticipantEventListeneronParticipantVideoResumed()Participant turned camera on
ParticipantEventListeneronParticipantVideoPaused()Participant turned camera off
ParticipantEventListeneronParticipantHandRaised()Participant raised hand
ParticipantEventListeneronParticipantHandLowered()Participant lowered hand
ParticipantEventListeneronParticipantStartedScreenShare()Participant started screen share
ParticipantEventListeneronParticipantStoppedScreenShare()Participant stopped screen share
ParticipantEventListeneronDominantSpeakerChanged()Active speaker changed
MediaEventsListeneronCameraFacingChanged()Camera switched front/back
ButtonClickListenerVarious button eventsIndividual button click tracking
LayoutListeneronCallLayoutChanged()Layout type changed
LayoutListeneronPictureInPictureLayoutEnabled/Disabled()PiP state changed

Call Logs

The CallLogRequest API is unchanged. The only difference is that auth is now handled by CometChatCalls.login() instead of passing the auth token manually.
CallLogRequest callLogRequest = CallLogRequest.CallLogRequestBuilder()
    .setLimit(30)
    .build();

callLogRequest.fetchNext(
  onSuccess: (List<CallLog> callLogs) {
    for (CallLog callLog in callLogs) {
      debugPrint("Session: ${callLog.sessionID}");
    }
  },
  onError: (CometChatCallsException e) {
    debugPrint("Error: ${e.message}");
  },
);

Deprecated Classes Summary

These classes still exist in v5 for backward compatibility but are deprecated:
Deprecated ClassReplacement
CallSettingsSessionSettings
CallSettingsBuilderSessionSettingsBuilder
CometChatCallsEventsListener5 focused listeners on CallSession
GenerateTokenCallToken
RTCUserParticipant
RTCMutedUserParticipantEventListener.onParticipantAudioMuted(Participant)
RTCRecordingInfoMediaEventsListener.onRecordingStarted() / onRecordingStopped()
CallSwitchRequestInfoRemoved (no replacement)
CometChatCalls.startSession()CometChatCalls.joinSession()
CometChatCalls.endSession()CallSession.getInstance()?.leaveSession()