Monitor participant activities with ParticipantEventListeners. This listener provides callbacks for participant join/leave events, audio/video state changes, hand raise actions, screen sharing, recording, and more.
Prerequisites
Register Listener
Register a ParticipantEventListeners to receive participant event callbacks:
final callSession = CallSession . getInstance ();
callSession ? . addParticipantEventListener ( ParticipantEventListeners (
onParticipantJoined : ( Participant participant) {
debugPrint ( " ${ participant . name } joined the call" );
},
onParticipantLeft : ( Participant participant) {
debugPrint ( " ${ participant . name } left the call" );
},
onParticipantListChanged : ( List < Participant > participants) {
debugPrint ( "Participant list updated: ${ participants . length } participants" );
},
// Additional callbacks...
));
Flutter listeners are not lifecycle-aware. You must manually remove listeners in your widget’s dispose() method to prevent memory leaks.
Participant Object
The Participant object contains information about a call participant:
Property Type Description uidStringUnique identifier of the participant nameStringDisplay name of the participant avatarStringAvatar URL of the participant isAudioMutedboolWhether audio is muted isVideoPausedboolWhether video is paused isHandRaisedboolWhether hand is raised isScreenSharingboolWhether screen is being shared
Callbacks
onParticipantJoined
Triggered when a new participant joins the call.
onParticipantJoined : ( Participant participant) {
debugPrint ( " ${ participant . name } joined the call" );
// Show join notification
// Update participant grid
}
Use Cases:
Display join notification/toast
Update participant count
Play join sound
onParticipantLeft
Triggered when a participant leaves the call.
onParticipantLeft : ( Participant participant) {
debugPrint ( " ${ participant . name } left the call" );
// Show leave notification
// Update participant grid
}
Use Cases:
Display leave notification
Update participant count
Play leave sound
onParticipantListChanged
Triggered when the participant list changes (join, leave, or any update).
onParticipantListChanged : ( List < Participant > participants) {
debugPrint ( "Participants: ${ participants . length } " );
// Update participant list UI
setState (() {
_participants = participants;
});
}
Use Cases:
Refresh participant list/grid
Update participant count badge
Sync local state with server
onParticipantAudioMuted
Triggered when a participant mutes their audio.
onParticipantAudioMuted : ( Participant participant) {
debugPrint ( " ${ participant . name } muted their audio" );
// Show muted indicator on participant tile
}
onParticipantAudioUnmuted
Triggered when a participant unmutes their audio.
onParticipantAudioUnmuted : ( Participant participant) {
debugPrint ( " ${ participant . name } unmuted their audio" );
// Hide muted indicator on participant tile
}
onParticipantVideoPaused
Triggered when a participant pauses their video.
onParticipantVideoPaused : ( Participant participant) {
debugPrint ( " ${ participant . name } paused their video" );
// Show avatar or placeholder instead of video
}
onParticipantVideoResumed
Triggered when a participant resumes their video.
onParticipantVideoResumed : ( Participant participant) {
debugPrint ( " ${ participant . name } resumed their video" );
// Show video stream
}
onParticipantHandRaised
Triggered when a participant raises their hand.
onParticipantHandRaised : ( Participant participant) {
debugPrint ( " ${ participant . name } raised their hand" );
// Show hand raised indicator
// Optionally play notification sound
}
onParticipantHandLowered
Triggered when a participant lowers their hand.
onParticipantHandLowered : ( Participant participant) {
debugPrint ( " ${ participant . name } lowered their hand" );
// Hide hand raised indicator
}
onParticipantStartedScreenShare
Triggered when a participant starts sharing their screen.
onParticipantStartedScreenShare : ( Participant participant) {
debugPrint ( " ${ participant . name } started screen sharing" );
// Switch to screen share view
// Show screen share indicator
}
onParticipantStoppedScreenShare
Triggered when a participant stops sharing their screen.
onParticipantStoppedScreenShare : ( Participant participant) {
debugPrint ( " ${ participant . name } stopped screen sharing" );
// Switch back to normal view
// Hide screen share indicator
}
onParticipantStartedRecording
Triggered when a participant starts recording the session.
onParticipantStartedRecording : ( Participant participant) {
debugPrint ( " ${ participant . name } started recording" );
// Show recording indicator
// Notify other participants
}
onParticipantStoppedRecording
Triggered when a participant stops recording the session.
onParticipantStoppedRecording : ( Participant participant) {
debugPrint ( " ${ participant . name } stopped recording" );
// Hide recording indicator
}
onDominantSpeakerChanged
Triggered when the dominant speaker changes (the participant currently speaking the loudest).
onDominantSpeakerChanged : ( Participant participant) {
debugPrint ( " ${ participant . name } is now the dominant speaker" );
// Highlight the dominant speaker's tile
// Auto-focus on dominant speaker in spotlight mode
}
Use Cases:
Highlight active speaker in UI
Auto-switch spotlight to dominant speaker
Show speaking indicator animation
Complete Example
Here’s a complete example handling all participant events:
import 'package:cometchat_calls_sdk/cometchat_calls_sdk.dart' ;
import 'package:flutter/material.dart' ;
class CallScreen extends StatefulWidget {
const CallScreen ({ super .key});
@override
State < CallScreen > createState () => _CallScreenState ();
}
class _CallScreenState extends State < CallScreen > {
CallSession ? _callSession;
ParticipantEventListeners ? _participantEventListener;
List < Participant > _participants = [];
@override
void initState () {
super . initState ();
_callSession = CallSession . getInstance ();
_setupParticipantEventListener ();
}
void _setupParticipantEventListener () {
_participantEventListener = ParticipantEventListeners (
onParticipantJoined : ( Participant participant) {
_showSnackBar ( " ${ participant . name } joined" );
},
onParticipantLeft : ( Participant participant) {
_showSnackBar ( " ${ participant . name } left" );
},
onParticipantListChanged : ( List < Participant > participants) {
if (mounted) {
setState (() {
_participants = participants;
});
}
},
onParticipantAudioMuted : ( Participant participant) {
debugPrint ( " ${ participant . name } muted audio" );
},
onParticipantAudioUnmuted : ( Participant participant) {
debugPrint ( " ${ participant . name } unmuted audio" );
},
onParticipantVideoPaused : ( Participant participant) {
debugPrint ( " ${ participant . name } paused video" );
},
onParticipantVideoResumed : ( Participant participant) {
debugPrint ( " ${ participant . name } resumed video" );
},
onParticipantHandRaised : ( Participant participant) {
_showSnackBar ( " ${ participant . name } raised their hand" );
},
onParticipantHandLowered : ( Participant participant) {
debugPrint ( " ${ participant . name } lowered their hand" );
},
onParticipantStartedScreenShare : ( Participant participant) {
_showSnackBar ( " ${ participant . name } is sharing their screen" );
},
onParticipantStoppedScreenShare : ( Participant participant) {
debugPrint ( " ${ participant . name } stopped screen sharing" );
},
onParticipantStartedRecording : ( Participant participant) {
_showSnackBar ( " ${ participant . name } started recording" );
},
onParticipantStoppedRecording : ( Participant participant) {
debugPrint ( " ${ participant . name } stopped recording" );
},
onDominantSpeakerChanged : ( Participant participant) {
debugPrint ( " ${ participant . name } is now the dominant speaker" );
},
);
_callSession ? . addParticipantEventListener (_participantEventListener ! );
}
void _showSnackBar ( String message) {
if (mounted) {
ScaffoldMessenger . of (context). showSnackBar (
SnackBar (content : Text (message)),
);
}
}
@override
void dispose () {
// Must manually remove listener to prevent memory leaks
if (_participantEventListener != null ) {
_callSession ? . removeParticipantEventListener (_participantEventListener ! );
}
super . dispose ();
}
@override
Widget build ( BuildContext context) {
return Scaffold (
body : Column (
children : [
Text ( "Participants: ${ _participants . length } " ),
Expanded (
child : ListView . builder (
itemCount : _participants.length,
itemBuilder : (context, index) {
final participant = _participants[index];
return ListTile (
title : Text (participant.name ?? "" ),
subtitle : Text (participant.uid ?? "" ),
trailing : Row (
mainAxisSize : MainAxisSize .min,
children : [
if (participant.isAudioMuted == true )
const Icon ( Icons .mic_off, color : Colors .red),
if (participant.isVideoPaused == true )
const Icon ( Icons .videocam_off, color : Colors .red),
if (participant.isHandRaised == true )
const Icon ( Icons .back_hand, color : Colors .orange),
],
),
);
},
),
),
],
),
);
}
}
Remove Listener
Remove the listener in your widget’s dispose() method to prevent memory leaks:
@override
void dispose () {
if (_participantEventListener != null ) {
_callSession ? . removeParticipantEventListener (_participantEventListener ! );
}
super . dispose ();
}
Callbacks Summary
Callback Parameter Description onParticipantJoinedParticipantA participant joined the call onParticipantLeftParticipantA participant left the call onParticipantListChangedList<Participant>Participant list was updated onParticipantAudioMutedParticipantA participant muted their audio onParticipantAudioUnmutedParticipantA participant unmuted their audio onParticipantVideoPausedParticipantA participant paused their video onParticipantVideoResumedParticipantA participant resumed their video onParticipantHandRaisedParticipantA participant raised their hand onParticipantHandLoweredParticipantA participant lowered their hand onParticipantStartedScreenShareParticipantA participant started screen sharing onParticipantStoppedScreenShareParticipantA participant stopped screen sharing onParticipantStartedRecordingParticipantA participant started recording onParticipantStoppedRecordingParticipantA participant stopped recording onDominantSpeakerChangedParticipantThe dominant speaker changed
Next Steps
Media Events Listener Handle local media state changes
Participant Actions Control other participants