Listening for Events
The UserView Flutter SDK exposes all events as Dart Streams. Subscribe to them for reactive event handling.
Listening for Events
import 'dart:async';
import 'package:upscopeio_flutter_sdk/upscopeio_flutter_sdk.dart';
class MyWidget extends StatefulWidget {
const MyWidget({super.key});
@override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
final List<StreamSubscription> _subscriptions = [];
@override
void initState() {
super.initState();
_subscriptions.add(
Upscope.instance.connectionState.listen((state) {
switch (state) {
case ConnectionState.inactive:
print('Inactive');
case ConnectionState.connecting:
print('Connecting...');
case ConnectionState.connected:
print('Connected');
case ConnectionState.reconnecting:
print('Reconnecting...');
case ConnectionState.error:
print('Error');
}
}),
);
_subscriptions.add(
Upscope.instance.onSessionStarted.listen((_) {
print('Session started');
}),
);
_subscriptions.add(
Upscope.instance.onSessionEnded.listen((reason) {
switch (reason) {
case SessionEndReason.userStopped:
print('User ended session');
case SessionEndReason.agentStopped:
print('Agent ended session');
case SessionEndReason.timeout:
print('Session timed out');
case SessionEndReason.error:
print('Session error');
}
}),
);
_subscriptions.add(
Upscope.instance.onCustomMessageReceived.listen((msg) {
print('Message from ${msg.viewerId}: ${msg.message}');
}),
);
_subscriptions.add(
Upscope.instance.onError.listen((error) {
print('Error: ${error.code} - ${error.message}');
}),
);
_subscriptions.add(
Upscope.instance.onViewerJoined.listen((viewer) {
print('Viewer joined: ${viewer.name ?? viewer.id}');
}),
);
_subscriptions.add(
Upscope.instance.onViewerLeft.listen((viewerId) {
print('Viewer left: $viewerId');
}),
);
_subscriptions.add(
Upscope.instance.onViewerCountChanged.listen((count) {
print('Viewers: $count');
}),
);
_subscriptions.add(
Upscope.instance.remoteControlState.listen((state) {
print('Remote control: ${state.name}');
}),
);
_subscriptions.add(
Upscope.instance.fullDeviceSharingState.listen((state) {
print('Full device sharing: ${state.name}');
}),
);
_subscriptions.add(
Upscope.instance.onFullDeviceRequest.listen((requestId) {
// true proceeds to the system permission prompt; false declines
Upscope.instance.respondToFullDeviceRequest(requestId, true);
}),
);
}
@override
void dispose() {
for (final sub in _subscriptions) {
sub.cancel();
}
super.dispose();
}
@override
Widget build(BuildContext context) {
return const SizedBox.shrink();
}
}
Event Reference
| Stream | Type | Description |
|---|---|---|
connectionState | ConnectionState | Connection state changed. |
onSessionStarted | String? | A screen sharing session has started. Emits the agent's name, if available. |
onSessionEnded | SessionEndReason | A session has ended. Reason indicates why (userStopped, agentStopped, timeout, or error). |
onCustomMessageReceived | CustomMessage | A custom message was received from a viewer. Includes message and viewerId. |
onError | UpscopeError | An SDK error occurred. Includes code and message. |
onViewerJoined | Viewer | An agent started viewing the session. The Viewer includes id, name, and screen metrics. |
onViewerLeft | String | An agent stopped viewing the session. Emits the viewer ID. |
onViewerCountChanged | int | The total number of active viewers changed. |
remoteControlState | RemoteControlState | Emits when whether an agent has remote control of the device changes (ability to tap and scroll). Enum values: inactive, active. Independent of the session being active. |
fullDeviceSharingState | FullDeviceSharingState | Emits when whether full-device (entire screen) sharing is running changes, as opposed to default in-app screen sharing. Enum values: inactive, active. |
onFullDeviceRequest | String | Fires when an agent requests full-device sharing, before the system permission prompt. Emits a requestId. Respond with Upscope.instance.respondToFullDeviceRequest(requestId, accept) — true allows (proceeds to the system prompt), false declines (stays in in-app mode). If you don't listen, the SDK proceeds directly. |
Using StreamBuilder
For reactive UI updates, use StreamBuilder instead of manual subscriptions:
StreamBuilder<ConnectionState>(
stream: Upscope.instance.connectionState,
builder: (context, snapshot) {
if (!snapshot.hasData) return const SizedBox.shrink();
return Text('Status: ${snapshot.data!.name}');
},
)
