upscopeio_flutter_sdk 2026.7.2 copy "upscopeio_flutter_sdk: ^2026.7.2" to clipboard
upscopeio_flutter_sdk: ^2026.7.2 copied to clipboard

Flutter SDK for Upscope cobrowsing — screen sharing, annotations, and remote control

Upscope Flutter SDK #

Flutter plugin for Upscope cobrowsing — lets support agents view and interact with your users' screens in real-time, with no extra permissions required.

Requirements #

  • Flutter 3.19+
  • Dart SDK 3.3+
  • iOS 14.0+
  • Android API 26+ (Android 8.0)

Installation #

flutter pub add upscopeio_flutter_sdk

Or add it manually to your pubspec.yaml:

dependencies:
  upscopeio_flutter_sdk: ^2026.7.2

Then run:

flutter pub get

Quick Start #

1. Initialize #

Call initialize once, early in your app lifecycle (e.g. in main or your root widget's initState):

import 'package:upscopeio_flutter_sdk/upscopeio_flutter_sdk.dart';

await Upscope.instance.initialize(
  UpscopeConfiguration(apiKey: 'your_api_key'),
);

By default autoConnect: true, so the SDK connects immediately after initialization.

2. Identify the visitor #

await Upscope.instance.updateConnection(
  uniqueId: 'user-123',
  callName: 'Jane Smith',
  tags: ['premium', 'mobile'],
);

Call this any time visitor identity changes (e.g. after sign-in).

3. Show a lookup code to the user #

The lookup code lets a support agent start a session by entering a short code in the Upscope dashboard:

Upscope.instance.lookupCode.listen((code) {
  if (code != null) {
    print('Share this code with support: $code');
  }
});

That's all. The agent can now initiate a cobrowsing session from the dashboard.

Core Concepts #

Connection vs. Session #

  • Connection — persistent WebSocket link to Upscope servers. Established via connect(), torn down via disconnect(). Required before any session can start.
  • Session — an active cobrowsing window with an agent. Begins when an agent joins (or when requestAgent() is called), ends when either party stops it.

Reactive streams #

All state is exposed as Streams so you can drive your UI reactively without polling.

// Show connection status in a StreamBuilder
StreamBuilder<ConnectionState>(
  stream: Upscope.instance.connectionState,
  builder: (context, snapshot) {
    return Text(switch (snapshot.data) {
      ConnectionState.connected    => 'Ready',
      ConnectionState.connecting   => 'Connecting…',
      ConnectionState.reconnecting => 'Reconnecting…',
      ConnectionState.error        => 'Error',
      _                            => 'Offline',
    });
  },
);

Masking sensitive widgets #

Wrap any widget in UpscopeMasked to hide it from the agent's view. The region is replaced with a black rectangle in the screen capture; your users see the real content as normal.

UpscopeMasked(
  child: CreditCardField(),
)

UpscopeMasked tracks position automatically — it handles layout changes, device rotation, and scroll without any extra wiring.

Common Tasks #

Listen for session lifecycle events #

Upscope.instance.onSessionStarted.listen((_) {
  showBanner('Screen sharing started');
});

Upscope.instance.onSessionEnded.listen((reason) {
  showBanner('Session ended: $reason');
});

React to viewer changes #

Upscope.instance.onViewerJoined.listen((viewer) {
  print('Agent joined: ${viewer.name}');
});

Upscope.instance.onViewerCountChanged.listen((count) {
  print('Viewers watching: $count');
});

Remote control and full-device sharing #

Observe and revoke agent device control, and respond to full-device sharing requests:

Upscope.instance.remoteControlState.listen((state) {
  print('Remote control: ${state.name}'); // inactive | pendingRequest | active
});

Upscope.instance.fullDeviceSharingState.listen((state) {
  print('Full-device sharing: ${state.name}'); // inactive | pendingRequest | active
});

// Stop an in-progress agent interaction (session stays active)
await Upscope.instance.stopRemoteControl();

// Stop full-device screen sharing
await Upscope.instance.stopFullDeviceSharing();

// Approve or decline a full-device sharing request
Upscope.instance.onFullDeviceRequest.listen((request) async {
  final allow = await askUser(request.agentName);
  await Upscope.instance
      .respondToFullDeviceRequest(request.requestId, accept: allow);
});

// Approve or decline a remote-control request
Upscope.instance.onControlRequest.listen((request) async {
  final allow = await askUser(request.agentName);
  await Upscope.instance
      .respondToControlRequest(request.requestId, accept: allow);
});

The pendingRequest state lets you build a fully custom accept/decline UI driven purely off the state stream: render your prompt while the state is pendingRequest, then respond via respondToControlRequest / respondToFullDeviceRequest. The onControlRequest / onFullDeviceRequest events each carry the requesting agentName (nullable) alongside the requestId.

Full-device sharing on iOS requires a Broadcast Upload Extension target — see Full device sharing (iOS).

Request an agent #

Proactively ask for a support agent without waiting for them to initiate:

await Upscope.instance.requestAgent();

// Cancel if the user changes their mind
await Upscope.instance.cancelAgentRequest();

Stop a session #

await Upscope.instance.stopSession();

Send a custom message to the agent #

await Upscope.instance.sendCustomMessage('{"event": "checkout_started"}');

Receive messages from the agent:

Upscope.instance.onCustomMessageReceived.listen((msg) {
  print('From ${msg.viewerId}: ${msg.message}');
});

Reset the connection #

Clears stored identity and reconnects (useful on sign-out):

// Reset and reconnect
await Upscope.instance.reset();

// Reset without reconnecting
await Upscope.instance.reset(reconnect: false);

Handle errors #

Upscope.instance.onError.listen((error) {
  print('Upscope error [${error.code}]: ${error.message}');
});

Configuration Reference #

All options are set once in UpscopeConfiguration passed to initialize:

Option Type Default Description
apiKey String required Your Upscope API key
autoConnect bool true Connect automatically on initialize
requireAuthorizationForSession bool true Prompt user before allowing agent to view screen
authorizationPromptTitle String 'Screen Sharing Request' Title of the consent dialog
authorizationPromptMessage String 'An agent wants to view your screen. Do you accept?' Body of the consent dialog
showTerminateButton bool true Show a stop-sharing button in the session banner
stopSessionText String 'Stop Sharing' Label on the stop-sharing button
endOfSessionMessage String? null Message shown to the user when the session ends
allowRemoteClick bool? null Override server-side remote click permission
allowRemoteScroll bool? null Override server-side remote scroll permission
requireControlRequest bool? null Require explicit user approval before agent can interact
region String? null Force a specific Upscope server region
broadcastAppGroupId String? null iOS only — App Group shared with the broadcast extension
broadcastExtensionBundleId String? null iOS only — Bundle ID of the broadcast upload extension

Full device sharing (iOS) #

Full device sharing lets the agent see the entire device screen (other apps, home screen) via ReplayKit. It requires a Broadcast Upload Extension target in your app — Flutter plugins can't add extension targets, so this is a one-time manual setup (~10 minutes). Without it, the agent's "Full app sharing" button is hidden.

Android needs no setup: it uses MediaProjection, and the required foreground-service permissions are merged automatically from the SDK's manifest.

1. Add the extension target #

In Xcode: File → New → Target → Broadcast Upload Extension. Name it (e.g. YourAppBroadcast), uncheck "Include UI Extension".

2. Replace the generated SampleHandler #

Open YourAppBroadcast/SampleHandler.swift (created by Xcode) and replace its contents:

import UpscopeSDK

class SampleHandler: UpscopeSampleHandler {}

3. Add an App Group to BOTH targets #

In Signing & Capabilities for your app target and the extension target, add the App Groups capability with the same group, e.g. group.com.yourcompany.yourapp.

4. Declare the group in the extension's Info.plist #

<key>UpscopeAppGroupId</key>
<string>group.com.yourcompany.yourapp</string>

Also make sure NSExtension > RPBroadcastProcessMode is 2 (sample-buffer mode); some Xcode template versions omit it. See the example app's Info.plist.

5. Add the extension pod #

# ios/Podfile (top level, alongside the Runner target)
target 'YourAppBroadcast' do
  pod 'UpscopeSDK/BroadcastExtension'
end

Run pod install.

6. Pass the keys at initialization #

await Upscope.instance.initialize(
  UpscopeConfiguration(
    apiKey: 'your_api_key',
    broadcastAppGroupId: 'group.com.yourcompany.yourapp',
    broadcastExtensionBundleId: 'com.yourcompany.yourapp.YourAppBroadcast',
  ),
);

broadcastAppGroupId enables the feature (the agent's button appears); broadcastExtensionBundleId preselects your extension in the iOS picker.

Notes #

  • The App Group ID must match in three places: both targets' entitlements, the extension's Info.plist (UpscopeAppGroupId), and broadcastAppGroupId.
  • Test on a physical device — ReplayKit broadcasts are unreliable on the simulator.
  • Full-device capture cannot mask views. Set disableFullScreenWhenMasked: true to automatically end full-device sharing whenever masked content is on screen.
  • Add-to-app setups: the extension target must be added to the host iOS project, not the Flutter module.
  • Without the extension, screen-only and visitor-app sharing still work; only full-device sharing is unavailable.
  • A complete working setup is in example/ios (RunnerBroadcast target).

API Reference #

Upscope.instance — methods #

Method Returns Description
initialize(UpscopeConfiguration) Future<void> Initialize and optionally auto-connect
connect() Future<void> Connect to Upscope servers
disconnect() Future<void> Disconnect from Upscope servers
reset({bool reconnect}) Future<void> Clear identity and optionally reconnect
updateConnection({uniqueId, callName, tags, identities, metadata}) Future<void> Update visitor identity
stopSession() Future<void> End the current cobrowsing session
requestAgent() Future<void> Request a support agent
cancelAgentRequest() Future<void> Cancel a pending agent request
getLookupCode() Future<void> Trigger a lookup code refresh
getShortId() Future<String?> Get the persistent visitor short ID
getWatchLink() Future<String?> Get a direct watch link for the current session
sendCustomMessage(String) Future<void> Send a custom string to all connected agents
stopRemoteControl() Future<void> Revoke agent device control (session continues)
stopFullDeviceSharing() Future<void> Stop full-device screen sharing
respondToFullDeviceRequest(String, {required bool accept}) Future<void> Approve/decline a full-device sharing request
respondToControlRequest(String, {required bool accept}) Future<void> Approve/decline a remote-control request

Upscope.instance — streams #

Stream Type Emits
connectionState Stream<ConnectionState> Connection state changes
sessionState Stream<SessionState> Session state changes
lookupCode Stream<String?> Current lookup code (refreshes periodically)
shortId Stream<String?> Persistent visitor short ID
onSessionStarted Stream<String?> Session ID when a session begins
onSessionEnded Stream<SessionEndReason> Reason the session ended
onViewerJoined Stream<Viewer> Agent joined the session
onViewerLeft Stream<String> Agent ID when an agent leaves
onViewerCountChanged Stream<int> Current number of watching agents
onCustomMessageReceived Stream<CustomMessage> Custom message from an agent
remoteControlState Stream<RemoteControlState> Agent device-control state changes
fullDeviceSharingState Stream<FullDeviceSharingState> Full-device sharing state changes
onFullDeviceRequest Stream<FullDeviceRequest> Request ID + agent name when an agent asks for full device
onControlRequest Stream<ControlRequest> Request ID + agent name when an agent asks for control
onError Stream<UpscopeError> SDK-level errors

Enums #

ConnectionStateinactive, connecting, connected, reconnecting, error

SessionStateinactive, pendingRequest, active, paused, ended

SessionEndReasonuserStopped, agentStopped, timeout, error

RemoteControlStateinactive, pendingRequest, active

FullDeviceSharingStateinactive, pendingRequest, active

Widgets #

UpscopeMasked({required Widget child}) — Wraps a child and masks its screen region during cobrowsing. Handles position tracking, rotation, and scroll automatically. No configuration needed.

Troubleshooting #

Sessions never start / connection stays inactive

  • Confirm autoConnect: true (default) or that you called connect() explicitly.
  • Verify the API key is correct for your Upscope account.
  • Check that the device has internet access.

Streams receive no events

  • Subscribe to streams before calling initialize, or use a StreamController with sync: false to avoid missing the first emission.

Masked widget position is wrong after navigation

  • UpscopeMasked tracks metrics changes and scroll, but not route transitions. If you push/pop routes while a masked widget is off-screen, the rect will be corrected on the next frame when the widget is visible again. This is typically transparent to the agent.

iOS build fails with missing plugin

  • Ensure you have run pod install (or flutter pub get which runs it for you).
  • The plugin class is UpscopePlugin registered under the io.upscope.flutter namespace.

Android build fails

  • Minimum supported API is 26. Check minSdk in your app-level build.gradle.

Support #

License #

Proprietary. See your agreement with Upscope for details.

0
likes
0
points
542
downloads

Publisher

verified publisherupscope.io

Weekly Downloads

Flutter SDK for Upscope cobrowsing — screen sharing, annotations, and remote control

Homepage

License

unknown (license)

Dependencies

flutter

More

Packages that depend on upscopeio_flutter_sdk

Packages that implement upscopeio_flutter_sdk