> ## 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.

# Participant Event Listener

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

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

## Register Listener

Register a `ParticipantEventListeners` to receive participant event callbacks:

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

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

***

## Participant Object

The `Participant` object contains information about a call participant:

| Property          | Type     | Description                          |
| ----------------- | -------- | ------------------------------------ |
| `uid`             | `String` | Unique identifier of the participant |
| `name`            | `String` | Display name of the participant      |
| `avatar`          | `String` | Avatar URL of the participant        |
| `isAudioMuted`    | `bool`   | Whether audio is muted               |
| `isVideoPaused`   | `bool`   | Whether video is paused              |
| `isHandRaised`    | `bool`   | Whether hand is raised               |
| `isScreenSharing` | `bool`   | Whether screen is being shared       |

***

## Callbacks

### onParticipantJoined

Triggered when a new participant joins the call.

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

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

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

```dart theme={null}
onParticipantAudioMuted: (Participant participant) {
  debugPrint("${participant.name} muted their audio");
  // Show muted indicator on participant tile
}
```

***

### onParticipantAudioUnmuted

Triggered when a participant unmutes their audio.

```dart theme={null}
onParticipantAudioUnmuted: (Participant participant) {
  debugPrint("${participant.name} unmuted their audio");
  // Hide muted indicator on participant tile
}
```

***

### onParticipantVideoPaused

Triggered when a participant pauses their video.

```dart theme={null}
onParticipantVideoPaused: (Participant participant) {
  debugPrint("${participant.name} paused their video");
  // Show avatar or placeholder instead of video
}
```

***

### onParticipantVideoResumed

Triggered when a participant resumes their video.

```dart theme={null}
onParticipantVideoResumed: (Participant participant) {
  debugPrint("${participant.name} resumed their video");
  // Show video stream
}
```

***

### onParticipantHandRaised

Triggered when a participant raises their hand.

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

```dart theme={null}
onParticipantHandLowered: (Participant participant) {
  debugPrint("${participant.name} lowered their hand");
  // Hide hand raised indicator
}
```

***

### onParticipantStartedScreenShare

Triggered when a participant starts sharing their screen.

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

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

```dart theme={null}
onParticipantStartedRecording: (Participant participant) {
  debugPrint("${participant.name} started recording");
  // Show recording indicator
  // Notify other participants
}
```

***

### onParticipantStoppedRecording

Triggered when a participant stops recording the session.

```dart theme={null}
onParticipantStoppedRecording: (Participant participant) {
  debugPrint("${participant.name} stopped recording");
  // Hide recording indicator
}
```

***

### onDominantSpeakerChanged

Triggered when the dominant speaker changes (the participant currently speaking the loudest).

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

```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;
  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:

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

***

## Callbacks Summary

| Callback                          | Parameter           | Description                          |
| --------------------------------- | ------------------- | ------------------------------------ |
| `onParticipantJoined`             | `Participant`       | A participant joined the call        |
| `onParticipantLeft`               | `Participant`       | A participant left the call          |
| `onParticipantListChanged`        | `List<Participant>` | Participant list was updated         |
| `onParticipantAudioMuted`         | `Participant`       | A participant muted their audio      |
| `onParticipantAudioUnmuted`       | `Participant`       | A participant unmuted their audio    |
| `onParticipantVideoPaused`        | `Participant`       | A participant paused their video     |
| `onParticipantVideoResumed`       | `Participant`       | A participant resumed their video    |
| `onParticipantHandRaised`         | `Participant`       | A participant raised their hand      |
| `onParticipantHandLowered`        | `Participant`       | A participant lowered their hand     |
| `onParticipantStartedScreenShare` | `Participant`       | A participant started screen sharing |
| `onParticipantStoppedScreenShare` | `Participant`       | A participant stopped screen sharing |
| `onParticipantStartedRecording`   | `Participant`       | A participant started recording      |
| `onParticipantStoppedRecording`   | `Participant`       | A participant stopped recording      |
| `onDominantSpeakerChanged`        | `Participant`       | The dominant speaker changed         |

## Next Steps

<CardGroup cols={2}>
  <Card title="Media Events Listener" icon="video" href="/calls/flutter/media-events-listener">
    Handle local media state changes
  </Card>

  <Card title="Participant Actions" icon="user-gear" href="/calls/flutter/participant-actions">
    Control other participants
  </Card>
</CardGroup>
