Documentation

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

StreamTypeDescription
connectionStateConnectionStateConnection state changed.
onSessionStartedString?A screen sharing session has started. Emits the agent's name, if available.
onSessionEndedSessionEndReasonA session has ended. Reason indicates why (userStopped, agentStopped, timeout, or error).
onCustomMessageReceivedCustomMessageA custom message was received from a viewer. Includes message and viewerId.
onErrorUpscopeErrorAn SDK error occurred. Includes code and message.
onViewerJoinedViewerAn agent started viewing the session. The Viewer includes id, name, and screen metrics.
onViewerLeftStringAn agent stopped viewing the session. Emits the viewer ID.
onViewerCountChangedintThe total number of active viewers changed.
remoteControlStateRemoteControlStateEmits 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.
fullDeviceSharingStateFullDeviceSharingStateEmits when whether full-device (entire screen) sharing is running changes, as opposed to default in-app screen sharing. Enum values: inactive, active.
onFullDeviceRequestStringFires 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}');
  },
)