hindsight_flutter 0.9.8 copy "hindsight_flutter: ^0.9.8" to clipboard
hindsight_flutter: ^0.9.8 copied to clipboard

Session replay for Flutter tree/state-delta capture with compact scene-graph encoding.

hindsight_flutter #

rrweb-style session replay for Flutter: the SDK captures the app as a structured scene graph (text, visuals, scroll containers, overlays) and streams compact deltas to the hindsight platform; the companion web player reconstructs the session pixel-faithfully, including scrolling, route transitions and keyboards.

Quick start #

Future<void> main() async {
  await Hindsight.init(HindsightConfig(
    projectId: 'proj_abc123',   // the proj_… id from your dashboard
    apiKey: 'hs_live_xxx',      // the hs_… ingest key for that project
  ));
  runApp(HindsightWidget(child: const MyApp()));
}

That's it — sessions stream to the hindsight platform. Business customers with a dedicated ingest or replay environment point ingestUrl / replayBaseUrl at their own endpoints.

Route changes are captured by adding the SDK's observer to your navigator:

MaterialApp(
  navigatorObservers: [Hindsight.navigatorObserver],
)

An observer can only attach to one navigator, so nested navigators (tab scaffolds, auth shells, ...) each get their own:

Navigator(
  observers: [Hindsight.createNavigatorObserver()],
)

Runtime controls #

All controls live on the static Hindsight surface:

Hindsight.identify('user-42', attributes: {'plan': 'pro'});
Hindsight.addEvent('checkout_started', {'cart_total': 42900});
await Hindsight.flush();

// Deep link to this session in the replay viewer — attach it to error
// reports, support tickets, logs.
final url = Hindsight.replayUrl; // <base>/?session=<sid>&project_id=<pid>

// Error marker on the replay timeline + forced flush, so the moment
// survives a crashing app.
Hindsight.captureError(error, stackTrace: stack, handled: false);

// Foreign events (analytics, navigation, network, ...) as breadcrumbs.
Hindsight.addBreadcrumb('http', {'url': '/cart', 'status_code': 500});

// Config can change at runtime; identity-level changes (userId,
// externalSessionId, ...) apply in place, pipeline-level changes restart
// the recording session.
Hindsight.updateConfig(HindsightConfig(
  projectId: 'proj_abc123',
  apiKey: 'hs_live_xxx',
  userId: 'user-42',
));

Vendor adapters #

replayUrl / captureError / addBreadcrumb are the hooks vendor adapter packages build on, so error reports in your existing tooling deep-link straight to the replay. hindsight_sentry is the reference adapter:

options.addIntegration(HindsightSentryIntegration());

Adapters resolve the Hindsight singleton when their host SDK starts them; Datadog / PostHog / Mixpanel adapters follow the same shape.

If your app uses flutter_svg, install the optional hindsight_flutter_svg adapter before Hindsight.init() to capture bundled SvgPicture.asset icons as vector UI chrome:

HindsightFlutterSvg.install();
await Hindsight.init(config);

Advanced: explicit clients #

The static surface fronts a single HindsightClient. Tests, harnesses and multi-client setups can construct clients directly and inject them, bypassing the singleton entirely:

final client = HindsightClient(config: HindsightConfig(...));
runApp(HindsightWidget(client: client, child: const MyApp()));

// Descendants reach the mounted client without a global:
final client = HindsightWidget.of(context);      // throws without an ancestor
final maybe = HindsightWidget.maybeOf(context);  // null without an ancestor

Privacy #

Hindsight masks only what must be masked: static UI text is captured as-is so replays stay legible, while everything the user types is masked by default (maskAllInputs: true). Obscured fields (passwords/PINs) are always masked, with no opt-out.

Redact sensitive static content, or reveal a specific subtree:

HindsightRedact(redactText: true, child: CreditCardSummary())   // mask this subtree
HindsightPrivacyScope(redactText: false, child: PublicBanner())  // opt out
Hindsight.mask<TransactionRow>();                                // mask by widget type

Tune the global defaults via HindsightPrivacyOptions:

const HindsightConfig(
  projectId: 'proj_...',
  apiKey: 'hs_...',
  privacy: HindsightPrivacyOptions(
    maskAllText: false,   // default: static text visible
    maskAllInputs: true,  // default: typed input masked (passwords always)
  ),
);

Sampling #

HindsightConfig.sessionSampleRate decides once per session start whether the session records at all; sampled-out sessions cost nothing.

How capture works (overview) #

  • One self-contained full snapshot at start and every treeFullSnapshotInterval (default 60s) — a lost chunk only costs the deltas up to the next full.
  • Between fulls, per-capture deltas: upserts/patches/removes plus scroll rebases (one translation per container instead of per-node patches) and payload-level fractional ordering keys.
  • Scroll positions stream as compact per-container traces ([epoch_ms, pixels] pairs); the player interpolates between keyframes and replays scroll motion from the traces.
0
likes
130
points
79
downloads

Documentation

API reference

Publisher

verified publisherunlimit.ing

Weekly Downloads

Session replay for Flutter tree/state-delta capture with compact scene-graph encoding.

License

Apache-2.0 (license)

Dependencies

archive, flutter, http

More

Packages that depend on hindsight_flutter

Packages that implement hindsight_flutter