Skip to main content

Events

The SDK emits events for everything that happens in the classroom. Use them to keep your app in sync with the live session.

Receiving Events

Use the onEvent callback on VerrifloPlayer:

VerrifloPlayer(
iframeUrl: iframeUrl,
onEvent: (event) {
print('Event: ${event.type}');
// Handle the event
},
)

VerrifloEventType

All event types:

TypeDescription
connectedSuccessfully connected to the classroom
disconnectedLost connection or left the room
participantJoinedAnother participant joined
participantLeftAnother participant left
classEndedInstructor ended the class
participantKickedCurrent user was removed
reconnectingConnection dropped, trying to reconnect
reconnectedSuccessfully reconnected
trackSubscribedStarted receiving a media track
trackUnsubscribedStopped receiving a media track
qualityChangedVideo quality was adjusted
errorSomething went wrong

VerrifloEvent Class

Each event is a VerrifloEvent object with these properties:

class VerrifloEvent {
/// The event category
final VerrifloEventType type;

/// ID of the related participant (for participant events)
final String? participantId;

/// Display name of the related participant
final String? participantName;

/// Human-readable message
final String? message;

/// Reason code (for kicks, disconnects)
final String? reason;

/// Error object (for error events)
final dynamic error;

/// When the event occurred
final DateTime timestamp;
}

Event Details

connected

Fired when successfully joined the classroom.

if (event.type == VerrifloEventType.connected) {
print('Joined classroom');
// Maybe hide a loading indicator
setState(() => _isConnecting = false);
}

disconnected

Fired when the connection ends (intentionally or not).

if (event.type == VerrifloEventType.disconnected) {
print('Disconnected: ${event.reason}');
// event.reason might be: 'networkError', 'kicked', 'serverShutdown', etc.
}

participantJoined / participantLeft

Fired when other participants join or leave.

case VerrifloEventType.participantJoined:
print('${event.participantName} joined');
// Maybe show a toast
break;

case VerrifloEventType.participantLeft:
print('${event.participantName} left');
break;

classEnded

The instructor ended the session. You should handle this.

case VerrifloEventType.classEnded:
// Navigate away - the room is done
Navigator.of(context).pop();
showMessage('Class has ended');
break;

You can also use the convenience callback:

VerrifloPlayer(
iframeUrl: iframeUrl,
onClassEnded: () => Navigator.pop(context),
)

participantKicked

Current user was removed by the instructor. You should handle this.

case VerrifloEventType.participantKicked:
// event.reason contains why (if the instructor provided one)
showMessage(event.reason ?? 'You were removed from class');
Navigator.of(context).pop();
break;

Or use the convenience callback:

VerrifloPlayer(
iframeUrl: iframeUrl,
onKicked: (reason) {
showMessage(reason ?? 'Removed from class');
Navigator.pop(context);
},
)

reconnecting

Connection dropped, SDK is trying to reconnect.

case VerrifloEventType.reconnecting:
// Show a subtle indicator - don't panic the user
setState(() => _isReconnecting = true);
break;

The player shows a built-in "Reconnecting..." banner, but you might want to show your own UI too.

reconnected

Back online after a connection hiccup.

case VerrifloEventType.reconnected:
// Connection restored
setState(() => _isReconnecting = false);
showMessage('Back online');
break;

trackSubscribed / trackUnsubscribed

Media track events. Mostly for debugging—the SDK handles track management automatically.

case VerrifloEventType.trackSubscribed:
print('Now receiving track from ${event.participantId}');
break;

case VerrifloEventType.trackUnsubscribed:
print('Track ended from ${event.participantId}');
break;

qualityChanged

Video quality was adjusted (either automatically or by user).

case VerrifloEventType.qualityChanged:
print('Quality now: ${event.message}'); // 'auto', 'high', 'medium', 'low'
break;

error

Something went wrong. Check event.message and event.error.

case VerrifloEventType.error:
print('Error: ${event.message}');
// Log to your analytics
analytics.logError('verriflo_error', {
'message': event.message,
'error': event.error?.toString(),
});
break;

Or use the error callback:

VerrifloPlayer(
iframeUrl: iframeUrl,
onError: (message, error) {
print('Error: $message');
// Handle or log the error
},
)

Helper Properties

VerrifloEvent has some convenience getters:

/// Is this an error event?
event.isError // true for VerrifloEventType.error

/// Is this a terminating event (class ended or kicked)?
event.isTerminating // true for classEnded, participantKicked

/// Is this a connection-related event?
event.isConnectionEvent // true for connected, disconnected, reconnecting, reconnected

Complete Event Handler

Here's a comprehensive event handler:

void handleEvent(VerrifloEvent event) {
// Log all events for debugging
debugPrint('Verriflo: ${event.type} - ${event.message ?? ""}');

switch (event.type) {
case VerrifloEventType.connected:
_onConnected();
break;

case VerrifloEventType.disconnected:
_onDisconnected(event.reason);
break;

case VerrifloEventType.participantJoined:
_onParticipantJoined(event.participantName ?? 'Someone');
break;

case VerrifloEventType.participantLeft:
_onParticipantLeft(event.participantName ?? 'Someone');
break;

case VerrifloEventType.classEnded:
_onClassEnded();
break;

case VerrifloEventType.participantKicked:
_onKicked(event.reason);
break;

case VerrifloEventType.reconnecting:
_showReconnecting();
break;

case VerrifloEventType.reconnected:
_hideReconnecting();
break;

case VerrifloEventType.qualityChanged:
_onQualityChanged(event.message);
break;

case VerrifloEventType.error:
_onError(event.message ?? 'Unknown error', event.error);
break;

case VerrifloEventType.trackSubscribed:
case VerrifloEventType.trackUnsubscribed:
// Usually don't need to handle these
break;
}
}

void _onConnected() {
setState(() {
_isConnecting = false;
_isConnected = true;
});
}

void _onDisconnected(String? reason) {
if (reason == 'classEnded' || reason == 'kicked') {
// These have their own handlers
return;
}
// Unexpected disconnect
debugPrint('Disconnected: $reason');
}

void _onParticipantJoined(String name) {
// Optional: show a toast or update participant count
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('$name joined'),
duration: const Duration(seconds: 2),
behavior: SnackBarBehavior.floating,
),
);
}

void _onParticipantLeft(String name) {
// Optional notification
}

void _onClassEnded() {
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (_) => const ClassEndedPage()),
);
}

void _onKicked(String? reason) {
showDialog(
context: context,
barrierDismissible: false,
builder: (_) => AlertDialog(
title: const Text('Removed from class'),
content: Text(reason ?? 'You have been removed from this classroom.'),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop(); // Close dialog
Navigator.of(context).pop(); // Leave classroom
},
child: const Text('OK'),
),
],
),
);
}

void _showReconnecting() {
// Show a subtle banner, not a full-screen blocker
setState(() => _isReconnecting = true);
}

void _hideReconnecting() {
setState(() => _isReconnecting = false);
}

void _onQualityChanged(String? quality) {
// Maybe log for analytics
debugPrint('Quality changed to: $quality');
}

void _onError(String message, dynamic error) {
// Log to analytics
FirebaseAnalytics.instance.logEvent(
name: 'classroom_error',
parameters: {'message': message},
);
}

Which Events Should I Handle?

Minimum (required):

  • classEnded — Navigate away
  • participantKicked — Navigate away with message

Recommended:

  • reconnecting / reconnected — Show connection status
  • error — Log for debugging

Optional:

  • participantJoined / participantLeft — Nice-to-have notifications
  • qualityChanged — Analytics

Next: State Management — Track classroom lifecycle.