replayfy_flutter 0.0.7 copy "replayfy_flutter: ^0.0.7" to clipboard
replayfy_flutter: ^0.0.7 copied to clipboard

Session replay, product analytics and error monitoring for your Flutter apps on iOS and Android.

example/lib/main.dart

import 'dart:io';

import 'package:flutter/material.dart';
import 'package:replayfy_flutter/replayfy_flutter.dart';

void main() {
  // runZoned lets Replayfy capture print/debugPrint ($console) and uncaught
  // async errors ($exception) — the Flutter analogue of patching console.*.
  Replay.runZoned(() async {
    WidgetsFlutterBinding.ensureInitialized();

    // Android emulator reaches the host at 10.0.2.2; iOS simulator shares
    // the host network, so localhost works.
    final String apiHost = Platform.isAndroid
        ? 'http://10.0.2.2:4000'
        : 'http://localhost:4000';

    // Boot the SDK. The native engine takes over screenshots, taps,
    // performance, and crashes; the Dart layer adds masking + network +
    // console + error capture. recordNetwork/Console/Errors default on.
    await Replay.start(ReplayConfig(
      apiKey: 'rpl_pk_ef7e2fc8c7f952bcd0a69466e1a42a0625f9',
      apiHost: apiHost,
      maskAllInputs: false,
    ));

    // Headless crash-test hook. Run with
    //   flutter run --dart-define=REPLAY_AUTOCRASH=dart_fatal
    // to fire a crash a few seconds after start (enough for the session to
    // register) without needing a UI tap. Values: dart_fatal | dart_handled.
    const String autoCrash = String.fromEnvironment('REPLAY_AUTOCRASH');
    if (autoCrash.isNotEmpty) {
      Future<void>.delayed(const Duration(seconds: 6), () {
        debugPrint('[replayfy] auto-crash: $autoCrash');
        switch (autoCrash) {
          case 'dart_handled':
            // → FlutterError.onError → $exception (fatal:false)
            throw StateError('autocrash: handled dart error');
          case 'dart_fatal':
          default:
            // → PlatformDispatcher.onError → $exception (fatal:true)
            throw StateError('autocrash: fatal dart error');
        }
      });
    }

    runApp(const ExampleApp());
  });
}

class ExampleApp extends StatelessWidget {
  const ExampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Replayfy Flutter',
      theme: ThemeData(useMaterial3: true, colorSchemeSeed: Colors.indigo),
      // Auto-tag screens from named routes (native swizzling can't see
      // Flutter's Navigator).
      navigatorObservers: <NavigatorObserver>[ReplayNavigatorObserver()],
      home: const HomeScreen(),
    );
  }
}

class HomeScreen extends StatefulWidget {
  const HomeScreen({super.key});

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  String? _sessionId;

  @override
  void initState() {
    super.initState();
    Replay.tagScreenName('Home');

    // === Engine-stub bridge tests — each forwards to native; watch [ReplaySdk] ===
    Replay.enableAdvancedGestureRecognizer(true);       // → facade: enableAdvancedGestureRecognizer(true)
    Replay.allowShortBreakForAnotherApp(true, breakWindowMs: 5000); // → facade: allowShortBreakForAnotherApp(true, 5000ms)
    Replay.setMultiSessionRecord(true);                 // → facade: setMultiSessionRecord(true)
    Replay.setExcludedScreens(<String>['Secret']);      // → facade: setExcludedScreens([Secret])
    Replay.excludeScreen('Vault');                      // → facade: excludeScreen(Vault)

    // Screen-exclusion round-trip through the channel: enter 'Secret' (excluded
    // → PAUSED frames) then leave to 'Home' (→ RESUMED frames).
    Future<void>.delayed(const Duration(seconds: 5),
        () => Replay.tagScreenName('Secret'));
    Future<void>.delayed(const Duration(seconds: 8),
        () => Replay.tagScreenName('Home'));

    _refreshSession();
  }

  Future<void> _refreshSession() async {
    final String? id = await Replay.currentSessionId();
    if (mounted) setState(() => _sessionId = id);
  }

  // Demonstrates Dart HTTP capture: this request goes through dart:io's
  // HttpClient (invisible to the native interceptors), so the Dart-side proxy
  // records it. The ingest host is excluded, so we hit a different endpoint.
  Future<void> _makeRequest() async {
    final HttpClient client = HttpClient();
    try {
      final HttpClientRequest req =
          await client.getUrl(Uri.parse('https://api.github.com/zen'));
      final HttpClientResponse resp = await req.close();
      await resp.drain<void>();
    } catch (_) {
      // Network errors are fine for a demo button.
    } finally {
      client.close();
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Replayfy Flutter Example')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('Session: ${_sessionId ?? "—"}'),
            const SizedBox(height: 24),

            // Anything inside <ReplayMask> is masked in playback per its
            // style — blur (default), overlay (solid box), or pixelate
            // (mosaic). Bounds are tracked in Dart and pulled by the engine.
            const Card(
              child: Padding(
                padding: EdgeInsets.all(16),
                child: Column(
                  children: <Widget>[
                    ReplayMask(
                      child: Text('blur  4242 4242 4242 4242',
                          style: TextStyle(fontSize: 18, letterSpacing: 1)),
                    ),
                    SizedBox(height: 8),
                    ReplayMask(
                      maskStyle: ReplayMaskStyle.overlay,
                      child: Text('overlay  012-34-5678',
                          style: TextStyle(fontSize: 18, letterSpacing: 1)),
                    ),
                    SizedBox(height: 8),
                    ReplayMask(
                      maskStyle: ReplayMaskStyle.pixelate,
                      child: Text('pixelate  hunter2',
                          style: TextStyle(fontSize: 18, letterSpacing: 1)),
                    ),
                  ],
                ),
              ),
            ),
            const SizedBox(height: 24),

            FilledButton(
              onPressed: () => Replay.track('cta_tapped',
                  properties: <String, dynamic>{'screen': 'Home'}),
              child: const Text('Track event'),
            ),
            OutlinedButton(
              onPressed: _makeRequest,
              child: const Text('Make network request'),
            ),
            OutlinedButton(
              // Captured as a $console event via runZoned's print hook.
              onPressed: () => debugPrint('Replayfy demo log line'),
              child: const Text('Log a message'),
            ),
            OutlinedButton(
              // A throw in a callback is reported via FlutterError.onError,
              // which ReplayErrorCapture forwards as a $exception (fatal:false).
              onPressed: () => throw StateError('demo: handled error'),
              child: const Text('Trigger handled error'),
            ),
            OutlinedButton(
              // An uncaught async error escapes to PlatformDispatcher.onError,
              // forwarded as a $exception (fatal:true) — the Dart analogue of
              // an unhandled crash. The app keeps running (the engine catches
              // native signals separately).
              onPressed: () => Future<void>.delayed(
                Duration.zero,
                () => throw StateError('demo: fatal async error'),
              ),
              child: const Text('Trigger fatal async error'),
            ),
            TextButton(
              onPressed: () async {
                await Replay.startNewSession();
                await _refreshSession();
              },
              child: const Text('Start new session'),
            ),
            TextButton(
              onPressed: () => Replay.cancelSession(),
              child: const Text('cancelSession (discard)'),
            ),
          ],
        ),
      ),
    );
  }
}
1
likes
160
points
305
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Session replay, product analytics and error monitoring for your Flutter apps on iOS and Android.

Homepage
Repository (GitHub)
View/report issues

License

BSD-3-Clause (license)

Dependencies

flutter

More

Packages that depend on replayfy_flutter

Packages that implement replayfy_flutter