Skip to main content
Monitor local media state changes with MediaEventListeners. This listener provides callbacks for your own audio/video state changes, recording events, screen sharing, audio mode changes, and camera facing changes.

Prerequisites

Register Listener

Register a MediaEventListeners to receive media event callbacks:
final callSession = CallSession.getInstance();

callSession?.addMediaEventsListener(MediaEventListeners(
  onAudioMuted: () {
    debugPrint("Audio muted");
  },
  onAudioUnMuted: () {
    debugPrint("Audio unmuted");
  },
  onVideoPaused: () {
    debugPrint("Video paused");
  },
  onVideoResumed: () {
    debugPrint("Video resumed");
  },
  // Additional callbacks...
));
Flutter listeners are not lifecycle-aware. You must manually remove listeners in your widget’s dispose() method to prevent memory leaks.

Callbacks

onAudioMuted

Triggered when your local audio is muted.
onAudioMuted: () {
  debugPrint("Audio muted");
  // Update mute button UI state
  setState(() {
    _isAudioMuted = true;
  });
}
Use Cases:
  • Update mute button icon/state
  • Show muted indicator in UI
  • Sync UI with actual audio state

onAudioUnMuted

Triggered when your local audio is unmuted.
onAudioUnMuted: () {
  debugPrint("Audio unmuted");
  // Update mute button UI state
  setState(() {
    _isAudioMuted = false;
  });
}
Use Cases:
  • Update mute button icon/state
  • Hide muted indicator
  • Sync UI with actual audio state

onVideoPaused

Triggered when your local video is paused.
onVideoPaused: () {
  debugPrint("Video paused");
  // Update video button UI state
  setState(() {
    _isVideoPaused = true;
  });
  // Show avatar instead of video preview
}
Use Cases:
  • Update video toggle button state
  • Show avatar/placeholder in local preview
  • Sync UI with actual video state

onVideoResumed

Triggered when your local video is resumed.
onVideoResumed: () {
  debugPrint("Video resumed");
  // Update video button UI state
  setState(() {
    _isVideoPaused = false;
  });
  // Show video preview
}
Use Cases:
  • Update video toggle button state
  • Show local video preview
  • Sync UI with actual video state

onRecordingStarted

Triggered when session recording starts.
onRecordingStarted: () {
  debugPrint("Recording started");
  // Show recording indicator
  setState(() {
    _isRecording = true;
  });
  // Notify user that recording is active
}
Use Cases:
  • Display recording indicator (red dot)
  • Update recording button state
  • Show notification to participants

onRecordingStopped

Triggered when session recording stops.
onRecordingStopped: () {
  debugPrint("Recording stopped");
  // Hide recording indicator
  setState(() {
    _isRecording = false;
  });
}
Use Cases:
  • Hide recording indicator
  • Update recording button state
  • Show recording saved notification

onScreenShareStarted

Triggered when you start sharing your screen.
onScreenShareStarted: () {
  debugPrint("Screen sharing started");
  // Update screen share button state
  setState(() {
    _isScreenSharing = true;
  });
  // Show screen share preview
}
Use Cases:
  • Update screen share button state
  • Show “You are sharing” indicator
  • Minimize local video preview

onScreenShareStopped

Triggered when you stop sharing your screen.
onScreenShareStopped: () {
  debugPrint("Screen sharing stopped");
  // Update screen share button state
  setState(() {
    _isScreenSharing = false;
  });
  // Restore normal view
}
Use Cases:
  • Update screen share button state
  • Hide “You are sharing” indicator
  • Restore local video preview

onAudioModeChanged

Triggered when the audio output mode changes (speaker, earpiece, bluetooth).
onAudioModeChanged: (AudioMode audioMode) {
  debugPrint("Audio mode changed to: $audioMode");
  // Update audio mode button/icon
  setState(() {
    _currentAudioMode = audioMode;
  });
}
AudioMode Values:
ValueDescription
AudioMode.speakerAudio output through device speaker
AudioMode.earpieceAudio output through earpiece
AudioMode.bluetoothAudio output through connected Bluetooth device
Use Cases:
  • Update audio mode button icon
  • Show current audio output device
  • Handle Bluetooth connection/disconnection

onCameraFacingChanged

Triggered when the camera facing changes (front/rear).
onCameraFacingChanged: (CameraFacing facing) {
  debugPrint("Camera facing changed to: $facing");
  // Update camera switch button state
  setState(() {
    _currentCameraFacing = facing;
  });
}
CameraFacing Values:
ValueDescription
CameraFacing.frontFront-facing camera (selfie camera)
CameraFacing.rearRear-facing camera (main camera)
Use Cases:
  • Update camera switch button icon
  • Adjust UI for mirrored/non-mirrored preview
  • Track camera state

Complete Example

Here’s a complete example handling all media 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;
  MediaEventListeners? _mediaEventsListener;

  bool _isAudioMuted = false;
  bool _isVideoPaused = false;
  bool _isRecording = false;
  bool _isScreenSharing = false;
  AudioMode _currentAudioMode = AudioMode.speaker;
  CameraFacing _currentCameraFacing = CameraFacing.front;

  @override
  void initState() {
    super.initState();
    _callSession = CallSession.getInstance();
    _setupMediaEventsListener();
  }

  void _setupMediaEventsListener() {
    _mediaEventsListener = MediaEventListeners(
      onAudioMuted: () {
        if (mounted) {
          setState(() => _isAudioMuted = true);
        }
      },
      onAudioUnMuted: () {
        if (mounted) {
          setState(() => _isAudioMuted = false);
        }
      },
      onVideoPaused: () {
        if (mounted) {
          setState(() => _isVideoPaused = true);
        }
      },
      onVideoResumed: () {
        if (mounted) {
          setState(() => _isVideoPaused = false);
        }
      },
      onRecordingStarted: () {
        if (mounted) {
          setState(() => _isRecording = true);
          ScaffoldMessenger.of(context).showSnackBar(
            const SnackBar(content: Text("Recording started")),
          );
        }
      },
      onRecordingStopped: () {
        if (mounted) {
          setState(() => _isRecording = false);
          ScaffoldMessenger.of(context).showSnackBar(
            const SnackBar(content: Text("Recording stopped")),
          );
        }
      },
      onScreenShareStarted: () {
        if (mounted) {
          setState(() => _isScreenSharing = true);
          ScaffoldMessenger.of(context).showSnackBar(
            const SnackBar(content: Text("You are sharing your screen")),
          );
        }
      },
      onScreenShareStopped: () {
        if (mounted) {
          setState(() => _isScreenSharing = false);
        }
      },
      onAudioModeChanged: (AudioMode audioMode) {
        if (mounted) {
          setState(() => _currentAudioMode = audioMode);
        }
      },
      onCameraFacingChanged: (CameraFacing facing) {
        if (mounted) {
          setState(() => _currentCameraFacing = facing);
          debugPrint("Camera switched to: $facing");
        }
      },
    );

    _callSession?.addMediaEventsListener(_mediaEventsListener!);
  }

  @override
  void dispose() {
    // Must manually remove listener to prevent memory leaks
    if (_mediaEventsListener != null) {
      _callSession?.removeMediaEventsListener(_mediaEventsListener!);
    }
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          // Call UI content here
          if (_isRecording)
            Container(
              padding: const EdgeInsets.all(8),
              color: Colors.red,
              child: const Row(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Icon(Icons.fiber_manual_record, color: Colors.white, size: 12),
                  SizedBox(width: 4),
                  Text("Recording", style: TextStyle(color: Colors.white)),
                ],
              ),
            ),
        ],
      ),
      bottomNavigationBar: Row(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          IconButton(
            icon: Icon(_isAudioMuted ? Icons.mic_off : Icons.mic),
            onPressed: () async {
              await _callSession?.toggleMuteAudio();
            },
          ),
          IconButton(
            icon: Icon(_isVideoPaused ? Icons.videocam_off : Icons.videocam),
            onPressed: () async {
              await _callSession?.togglePauseVideo();
            },
          ),
          IconButton(
            icon: Icon(
              _currentAudioMode == AudioMode.speaker
                  ? Icons.volume_up
                  : _currentAudioMode == AudioMode.bluetooth
                      ? Icons.bluetooth_audio
                      : Icons.phone_in_talk,
            ),
            onPressed: () {
              // Show audio mode picker
            },
          ),
        ],
      ),
    );
  }
}

Remove Listener

Remove the listener in your widget’s dispose() method to prevent memory leaks:
@override
void dispose() {
  if (_mediaEventsListener != null) {
    _callSession?.removeMediaEventsListener(_mediaEventsListener!);
  }
  super.dispose();
}

Callbacks Summary

CallbackParameterDescription
onAudioMuted-Local audio was muted
onAudioUnMuted-Local audio was unmuted
onVideoPaused-Local video was paused
onVideoResumed-Local video was resumed
onRecordingStarted-Session recording started
onRecordingStopped-Session recording stopped
onScreenShareStarted-Local screen sharing started
onScreenShareStopped-Local screen sharing stopped
onAudioModeChangedAudioModeAudio output mode changed
onCameraFacingChangedCameraFacingCamera facing changed

Next Steps

Button Click Listener

Handle UI button click events

Audio Controls

Control audio programmatically