Flutter apps

The Flutter SDK relyo_flutter runs on iOS (15 and later), Android (8 and later) and Flutter Web with the same code. It transmits the Flutter UI as a sequence of images: only changed regions are sent, up to 10 images per second. No operating system permission is needed.

Dependency

The package is not on pub.dev but in the Relyo repository:

# pubspec.yaml
dependencies:
  relyo_flutter:
    git:
      url: https://github.com/justcode049/Relyo.git
      path: packages/flutter-sdk
      ref: main # better: pin a specific commit

The repository is not public yet. Until then, request access by email to [email protected].

Requires Flutter 3.38 or later.

  • iOS: deployment target 15.0 (platform :ios, '15.0' in the Podfile).
  • Android: android.permission.INTERNET in the manifest; for a local server via ws:// during development also android:usesCleartextTraffic="true".

Setup

import 'package:relyo_flutter/relyo_flutter.dart';

void main() {
  Relyo.init(
    relayUrl: 'wss://relyo.de',
    projectKey: 'pk_yourapp', // not needed with the open service
    connect: RelyoConnect.lazy,
    appVersion: '1.4.0',
  );
  runApp(RelyoScope(child: const MyApp()));
}

RelyoScope goes around MaterialApp or CupertinoApp and may only appear once. The SDK dialogs and the status bar live in their own overlay outside the transmitted area and therefore never appear in the supporter’s view.

Support entry

Ideally in the settings, unobtrusively at the end of the list:

TextButton(
  onPressed: () => Relyo.instance.showCode(context),
  child: const Text('Support'),
)

showCode shows the code dialog; after that the SDK guides through consent, session and end. During the session it enlarges the app’s top inset by the height of the status bar (36 pixels) so the bar does not cover any controls.

Options

ParameterDefaultMeaning
relayUrl–Required. Server address, for example wss://relyo.de.
projectKey–The app’s project key. Without a key, a server with the open service assigns the app to its app ID (bundle ID or package name).
user–Signed-in user, RelyoUser(id: …, name: …). Display only, not verified.
connectRelyoConnect.autoauto: with user connect at once, otherwise only on showCode; eager: always at once; lazy: only on showCode.
appVersion''App version; shown in the console.
fps5Target frame rate, 2 to 10. With limited bandwidth the SDK lowers it by itself.
consentText–Your own explanation in the consent dialog; {agent} is replaced by the supporter’s name.

Further calls:

Relyo.instance.identify(const RelyoUser(id: 'u_123', name: 'Max M.')); // after login
Relyo.instance.identify(null); // on logout
Relyo.instance.end(); // end the session, otherwise “Beenden” in the status bar

Before the first connection the SDK stores nothing on the device, not even an identifier.

Masking

Masking happens on the device before any image leaves it:

  • Always: every field with obscureText: true.
  • RelyoRedact: everything inside this widget.
RelyoRedact(
  child: TextField(decoration: InputDecoration(labelText: 'IBAN')),
)

Masked areas are painted black before the image is encoded. If the SDK cannot fully check the screen once, it blacks out the whole image. The SDK rejects remote input into masked fields.

So that the supporter sees where the user is and drawings disappear on page changes:

MaterialApp(
  navigatorObservers: [RelyoNavigatorObserver()],
  // …
)

With GoRouter, additionally for changes that do not trigger a navigator push (go(), ShellRoute):

final router = GoRouter(observers: [RelyoNavigatorObserver()], routes: [/* … */]);

router.routerDelegate.addListener(() {
  Relyo.instance.reportRoute(router.routerDelegate.currentConfiguration.uri.path);
});

Control

After the second consent, the supporter can tap, swipe and scroll, type text into the focused field and send Enter and Backspace. Any touch of the user’s own or a hardware key ends control at once.

Limitations

  • Platform views (WebView, maps, camera) appear as a grey area labelled “Eingebettete Ansicht”.
  • System dialogs (permissions, sharing) and the on-screen keyboard are not in the image.
  • Remote text only in Flutter text fields (TextField, CupertinoTextField and anything based on EditableText).
  • When the app goes to the background, transmission pauses and resumes when the user returns.
  • The dialogs are in German only so far.

Testing

For your own widget tests without a server, package:relyo_flutter/testing.dart provides an in-memory test server (RelyoTesting.install, FakeRelyoServer).

This page as Markdown