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:
| Type | Description |
|---|---|
connected | Successfully connected to the classroom |
disconnected | Lost connection or left the room |
participantJoined | Another participant joined |
participantLeft | Another participant left |
classEnded | Instructor ended the class |
participantKicked | Current user was removed |
reconnecting | Connection dropped, trying to reconnect |
reconnected | Successfully reconnected |
trackSubscribed | Started receiving a media track |
trackUnsubscribed | Stopped receiving a media track |
qualityChanged | Video quality was adjusted |
error | Something 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 awayparticipantKicked— Navigate away with message
Recommended:
reconnecting/reconnected— Show connection statuserror— Log for debugging
Optional:
participantJoined/participantLeft— Nice-to-have notificationsqualityChanged— Analytics
Next: State Management — Track classroom lifecycle.