uxlens_sdk

Session replay & analytics SDK for Flutter apps — captures screen frames, touch/gesture events, and navigation, with sensitive-field masking applied client-side before anything is buffered. This is the Phase 1 build: capture and local buffering only, no backend yet (see the top-level repo README for the phase roadmap).

Integration

void main() {
  // Single init call, before runApp.
  UxLens.start();
  runApp(
    UxLensRecorder(
      child: MaterialApp(
        navigatorObservers: [UxLens.instance!.navigatorObserver],
        home: const HomePage(),
      ),
    ),
  );
}

That's the entire integration surface. UxLensRecorder wraps your app in a RepaintBoundary + root gesture listener; the navigator observer tracks screen transitions.

What gets captured

  • Frames: RepaintBoundary screenshots at a tunable rate (default 3fps), PNG-encoded, with sensitive Semantics regions painted black before encoding.
  • Touch events: tap/pan/scroll, normalized to a 0–1 coordinate space so heatmaps line up across device sizes.
  • Navigation: route push/pop/replace/remove via NavigatorObserver.

All of it is batched in memory (EventBuffer) and flushed to <app documents>/uxlens_sessions/<sessionId>/ on a timer, when the buffer fills, or when the app backgrounds — never streamed record-by-record.

Masking

UxLensConfig.sensitiveLabelKeywords matches Semantics label/hint/value text (password, card number, email, etc); autoMaskPasswordFields catches obscured text fields regardless of label. Masking runs against the live Semantics tree and paints the matched region's rect black on the captured ui.Image before it's ever encoded or held as bytes — masked content never exists in memory in unmasked form past the paint step.

Config

const UxLensConfig({
  double frameRate = 3.0,
  Duration flushInterval = const Duration(seconds: 10),
  int maxBufferedRecords = 500,
  List<String> sensitiveLabelKeywords = const [...],
  bool autoMaskPasswordFields = true,
});

Example app

example/ is a 3-screen demo (login with a password field, a scrollable home list, and a session inspector) — the inspector screen reads back uxlens_sdk's own local dump so you can confirm capture/masking/buffering without a backend. Run it with fvm flutter run from example/.

Testing note

Widget tests that exercise UxLens.stop() (or anything that flushes to disk) must wrap that call in tester.runAsync(...)RenderRepaintBoundary.toImage() and file I/O are real engine/OS async work that never resolves inside testWidgets's fake-time zone otherwise. See example/test/widget_test.dart.

Libraries

uxlens_sdk
UxLens — session replay & analytics SDK for Flutter apps.