> ## Documentation Index
> Fetch the complete documentation index at: https://cometchat-22654f5b-docs-v6-beta2-flutter-uikit.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Media Events Listener

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

* An active [call session](/calls/flutter/join-session)
* Access to the `CallSession` instance

## Register Listener

Register a `MediaEventListeners` to receive media event callbacks:

```dart theme={null}
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...
));
```

<Warning>
  Flutter listeners are **not** lifecycle-aware. You must manually remove listeners in your widget's `dispose()` method to prevent memory leaks.
</Warning>

***

## Callbacks

### onAudioMuted

Triggered when your local audio is muted.

```dart theme={null}
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.

```dart theme={null}
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.

```dart theme={null}
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.

```dart theme={null}
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.

```dart theme={null}
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.

```dart theme={null}
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.

```dart theme={null}
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.

```dart theme={null}
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).

```dart theme={null}
onAudioModeChanged: (AudioMode audioMode) {
  debugPrint("Audio mode changed to: $audioMode");
  // Update audio mode button/icon
  setState(() {
    _currentAudioMode = audioMode;
  });
}
```

**AudioMode Values:**

| Value                 | Description                                     |
| --------------------- | ----------------------------------------------- |
| `AudioMode.speaker`   | Audio output through device speaker             |
| `AudioMode.earpiece`  | Audio output through earpiece                   |
| `AudioMode.bluetooth` | Audio 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).

```dart theme={null}
onCameraFacingChanged: (CameraFacing facing) {
  debugPrint("Camera facing changed to: $facing");
  // Update camera switch button state
  setState(() {
    _currentCameraFacing = facing;
  });
}
```

**CameraFacing Values:**

| Value                | Description                         |
| -------------------- | ----------------------------------- |
| `CameraFacing.front` | Front-facing camera (selfie camera) |
| `CameraFacing.rear`  | Rear-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:

```dart theme={null}
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:

```dart theme={null}
@override
void dispose() {
  if (_mediaEventsListener != null) {
    _callSession?.removeMediaEventsListener(_mediaEventsListener!);
  }
  super.dispose();
}
```

***

## Callbacks Summary

| Callback                | Parameter      | Description                  |
| ----------------------- | -------------- | ---------------------------- |
| `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 |
| `onAudioModeChanged`    | `AudioMode`    | Audio output mode changed    |
| `onCameraFacingChanged` | `CameraFacing` | Camera facing changed        |

## Next Steps

<CardGroup cols={2}>
  <Card title="Button Click Listener" icon="hand-pointer" href="/calls/flutter/button-click-listener">
    Handle UI button click events
  </Card>

  <Card title="Audio Controls" icon="microphone" href="/calls/flutter/audio-controls">
    Control audio programmatically
  </Card>
</CardGroup>
