Intercept UI button clicks with ButtonClickListeners. This listener provides callbacks when users tap buttons in the call UI, allowing you to implement custom behavior or show confirmation dialogs.
Prerequisites
Register Listener
Register a ButtonClickListeners to receive button click callbacks:
final callSession = CallSession . getInstance ();
callSession ? . addButtonClickListener ( ButtonClickListeners (
onLeaveSessionButtonClicked : () {
debugPrint ( "Leave button clicked" );
},
onToggleAudioButtonClicked : () {
debugPrint ( "Audio toggle button clicked" );
},
onToggleVideoButtonClicked : () {
debugPrint ( "Video toggle button clicked" );
},
// Additional callbacks...
));
Flutter listeners are not lifecycle-aware. You must manually remove listeners in your widget’s dispose() method to prevent memory leaks.
Callbacks
Triggered when the user taps the leave session button.
onLeaveSessionButtonClicked : () {
debugPrint ( "Leave button clicked" );
// Show confirmation dialog before leaving
showDialog (
context : context,
builder : (context) => AlertDialog (
title : const Text ( "Leave Call" ),
content : const Text ( "Are you sure you want to leave this call?" ),
actions : [
TextButton (
onPressed : () => Navigator . pop (context),
child : const Text ( "Cancel" ),
),
TextButton (
onPressed : () async {
Navigator . pop (context);
await CallSession . getInstance () ? . leaveSession ();
},
child : const Text ( "Leave" ),
),
],
),
);
}
Use Cases:
Show confirmation dialog before leaving
Log analytics event
Perform cleanup before leaving
Triggered when the user taps the audio mute/unmute button.
onToggleAudioButtonClicked : () {
debugPrint ( "Audio toggle clicked" );
// Track audio toggle analytics
}
Use Cases:
Log analytics events
Show tooltip on first use
Implement custom audio toggle logic
Triggered when the user taps the video on/off button.
onToggleVideoButtonClicked : () {
debugPrint ( "Video toggle clicked" );
// Track video toggle analytics
}
Use Cases:
Log analytics events
Check camera permissions
Implement custom video toggle logic
Triggered when the user taps the switch camera button.
onSwitchCameraButtonClicked : () {
debugPrint ( "Switch camera clicked" );
// Track camera switch analytics
}
Use Cases:
Log analytics events
Show camera switching animation
Track front/back camera usage
Triggered when the user taps the raise hand button.
onRaiseHandButtonClicked : () {
debugPrint ( "Raise hand clicked" );
// Show hand raised confirmation
ScaffoldMessenger . of (context). showSnackBar (
const SnackBar (content : Text ( "Hand raised" )),
);
}
Use Cases:
Show confirmation feedback
Log analytics events
Implement custom hand raise behavior
Triggered when the user taps the share invite button.
onShareInviteButtonClicked : () {
debugPrint ( "Share invite clicked" );
// Show custom share dialog
Share . share ( "Join my call: https://example.com/call/ $ sessionId " );
}
Use Cases:
Show custom share sheet
Generate and share invite link
Copy link to clipboard
Triggered when the user taps the change layout button.
onChangeLayoutButtonClicked : () {
debugPrint ( "Change layout clicked" );
// Show layout options dialog
showDialog (
context : context,
builder : (context) => SimpleDialog (
title : const Text ( "Select Layout" ),
children : [
SimpleDialogOption (
onPressed : () async {
Navigator . pop (context);
await CallSession . getInstance () ? . setLayout ( LayoutType .tile);
},
child : const Text ( "Tile" ),
),
SimpleDialogOption (
onPressed : () async {
Navigator . pop (context);
await CallSession . getInstance () ? . setLayout ( LayoutType .spotlight);
},
child : const Text ( "Spotlight" ),
),
],
),
);
}
Use Cases:
Show custom layout picker
Log layout change analytics
Implement custom layout switching
Triggered when the user taps the participant list button.
onParticipantListButtonClicked : () {
debugPrint ( "Participant list clicked" );
// Track participant list views
}
Use Cases:
Log analytics events
Show custom participant list UI
Track feature usage
Triggered when the user taps the chat button.
onChatButtonClicked : () {
debugPrint ( "Chat button clicked" );
// Open custom chat UI
Navigator . push (
context,
MaterialPageRoute (
builder : (context) => ChatScreen (sessionId : sessionId),
),
);
}
Use Cases:
Open custom chat interface
Show in-call messaging overlay
Navigate to chat screen
Triggered when the user taps the recording button.
onRecordingToggleButtonClicked : () {
debugPrint ( "Recording toggle clicked" );
// Show recording consent dialog
showDialog (
context : context,
builder : (context) => AlertDialog (
title : const Text ( "Start Recording" ),
content : const Text (
"All participants will be notified that this call is being recorded." ,
),
actions : [
TextButton (
onPressed : () => Navigator . pop (context),
child : const Text ( "Cancel" ),
),
TextButton (
onPressed : () async {
Navigator . pop (context);
await CallSession . getInstance () ? . startRecording ();
},
child : const Text ( "Start" ),
),
],
),
);
}
Use Cases:
Show recording consent dialog
Check recording permissions
Log recording analytics
Complete Example
Here’s a complete example handling all button click events:
import 'package:cometchat_calls_sdk/cometchat_calls_sdk.dart' ;
import 'package:flutter/material.dart' ;
class CallScreen extends StatefulWidget {
final String sessionId;
const CallScreen ({ super .key, required this .sessionId});
@override
State < CallScreen > createState () => _CallScreenState ();
}
class _CallScreenState extends State < CallScreen > {
CallSession ? _callSession;
ButtonClickListeners ? _buttonClickListener;
@override
void initState () {
super . initState ();
_callSession = CallSession . getInstance ();
_setupButtonClickListener ();
}
void _setupButtonClickListener () {
_buttonClickListener = ButtonClickListeners (
onLeaveSessionButtonClicked : () {
_showLeaveConfirmationDialog ();
},
onToggleAudioButtonClicked : () {
debugPrint ( "Audio toggle clicked" );
},
onToggleVideoButtonClicked : () {
debugPrint ( "Video toggle clicked" );
},
onSwitchCameraButtonClicked : () {
debugPrint ( "Switch camera clicked" );
},
onRaiseHandButtonClicked : () {
if (mounted) {
ScaffoldMessenger . of (context). showSnackBar (
const SnackBar (content : Text ( "Hand raised" )),
);
}
},
onShareInviteButtonClicked : () {
_shareCallLink ();
},
onChangeLayoutButtonClicked : () {
_showLayoutOptionsDialog ();
},
onParticipantListButtonClicked : () {
debugPrint ( "Participant list opened" );
},
onChatButtonClicked : () {
_openChatScreen ();
},
onRecordingToggleButtonClicked : () {
_showRecordingConsentDialog ();
},
);
_callSession ? . addButtonClickListener (_buttonClickListener ! );
}
void _showLeaveConfirmationDialog () {
showDialog (
context : context,
builder : (context) => AlertDialog (
title : const Text ( "Leave Call" ),
content : const Text ( "Are you sure you want to leave?" ),
actions : [
TextButton (
onPressed : () => Navigator . pop (context),
child : const Text ( "Cancel" ),
),
TextButton (
onPressed : () async {
Navigator . pop (context);
await _callSession ? . leaveSession ();
},
child : const Text ( "Leave" ),
),
],
),
);
}
void _shareCallLink () {
// Use share_plus or similar package
debugPrint ( "Share call link: ${ widget . sessionId } " );
}
void _showLayoutOptionsDialog () {
showDialog (
context : context,
builder : (context) => SimpleDialog (
title : const Text ( "Select Layout" ),
children : [
SimpleDialogOption (
onPressed : () async {
Navigator . pop (context);
await _callSession ? . setLayout ( LayoutType .tile);
},
child : const Text ( "Tile" ),
),
SimpleDialogOption (
onPressed : () async {
Navigator . pop (context);
await _callSession ? . setLayout ( LayoutType .spotlight);
},
child : const Text ( "Spotlight" ),
),
],
),
);
}
void _openChatScreen () {
// Navigate to chat screen
debugPrint ( "Open chat for session: ${ widget . sessionId } " );
}
void _showRecordingConsentDialog () {
showDialog (
context : context,
builder : (context) => AlertDialog (
title : const Text ( "Start Recording" ),
content : const Text ( "All participants will be notified." ),
actions : [
TextButton (
onPressed : () => Navigator . pop (context),
child : const Text ( "Cancel" ),
),
TextButton (
onPressed : () async {
Navigator . pop (context);
await _callSession ? . startRecording ();
},
child : const Text ( "Start" ),
),
],
),
);
}
@override
void dispose () {
// Must manually remove listener to prevent memory leaks
if (_buttonClickListener != null ) {
_callSession ? . removeButtonClickListener (_buttonClickListener ! );
}
super . dispose ();
}
@override
Widget build ( BuildContext context) {
return const Scaffold (
body : Center (
child : Text ( "Call UI" ),
),
);
}
}
Remove Listener
Remove the listener in your widget’s dispose() method to prevent memory leaks:
@override
void dispose () {
if (_buttonClickListener != null ) {
_callSession ? . removeButtonClickListener (_buttonClickListener ! );
}
super . dispose ();
}
Callbacks Summary
Callback Description onLeaveSessionButtonClickedLeave session button was tapped onToggleAudioButtonClickedAudio mute/unmute button was tapped onToggleVideoButtonClickedVideo on/off button was tapped onSwitchCameraButtonClickedSwitch camera button was tapped onRaiseHandButtonClickedRaise hand button was tapped onShareInviteButtonClickedShare invite button was tapped onChangeLayoutButtonClickedChange layout button was tapped onParticipantListButtonClickedParticipant list button was tapped onChatButtonClickedChat button was tapped onRecordingToggleButtonClickedRecording toggle button was tapped
You can hide specific buttons using SessionSettings :
final sessionSettings = CometChatCalls . SessionSettingsBuilder ()
. hideLeaveSessionButton ( false )
. hideToggleAudioButton ( false )
. hideToggleVideoButton ( false )
. hideSwitchCameraButton ( false )
. hideRaiseHandButton ( false )
. hideShareInviteButton ( true )
. hideChangeLayoutButton ( false )
. hideParticipantListButton ( false )
. hideChatButton ( true )
. hideRecordingButton ( true )
. build ();
Next Steps
Layout Listener Handle layout change events
Session Settings Configure button visibility