Session Replay records what a user saw and did in your Flutter application, so you can watch a bug happen instead of reconstructing it from a stack trace. Replays are linked to the RUM session they belong to, so you can move from an error straight to the recording.

Session Replay ships as a separate package, atatus_session_replay, and runs on top of RUM.

Note: This guide covers Android targets only. Session Replay for Flutter is in preview, and parts of its public API may change without a major version update.

Prerequisites

  • RUM is set up in your application. See Flutter Monitoring Setup.
  • Flutter 3.27.0 or higher and Dart SDK 3.6.0 or higher.

Setup

1. Add the package

Add atatus_session_replay alongside the plugin in your pubspec.yaml:

copy
icon/buttons/copy
dependencies:
  atatus_flutter_plugin: ^1.0.4
  atatus_session_replay: ^1.0.4

Then fetch the packages:

copy
icon/buttons/copy
flutter pub get

2. Enable Session Replay

Call enableSessionReplay on your AtatusConfiguration. RUM must be configured for replay to record anything:

copy
icon/buttons/copy
import 'package:atatus_flutter_plugin/atatus_flutter_plugin.dart';
import 'package:atatus_session_replay/atatus_session_replay.dart';

void main() {
  final configuration = AtatusConfiguration(
    licenseKey: '<LICENSE_KEY>',
    env: '<ENV_NAME>',
    appName: '<APP_NAME>',
    rumConfiguration: AtatusRumConfiguration(),
  )..enableSessionReplay(
      AtatusSessionReplayConfiguration(
        // Percentage of sampled RUM sessions that record a replay
        replaySampleRate: 30.0,
        textAndInputPrivacyLevel: TextAndInputPrivacyLevel.maskAll,
        imagePrivacyLevel: ImagePrivacyLevel.maskAll,
        touchPrivacyLevel: TouchPrivacyLevel.hide,
      ),
    );

  AtatusSdk.runApp(configuration, TrackingConsent.granted, () async {
    runApp(const MyApp());
  });
}

3. Add the capture widget

Add a SessionReplayCapture widget at the root of your widget tree, above MaterialApp or your equivalent application widget. A Key is required:

copy
icon/buttons/copy
class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  // A key is required for SessionReplayCapture
  final captureKey = GlobalKey();

  @override
  Widget build(BuildContext context) {
    return SessionReplayCapture(
      key: captureKey,
      rum: AtatusSdk.instance.rum!,
      sessionReplay: AtatusSessionReplay.instance,
      child: MaterialApp(
        home: const HomeScreen(),
      ),
    );
  }
}
Important: SessionReplayCapture already includes a RumUserActionDetector. If you added one during RUM setup, remove it and keep the one inside SessionReplayCapture, otherwise user actions are recorded twice.

Configuration Options

AtatusSessionReplayConfiguration accepts the following options:

Option Default Description
replaySampleRate Required Percentage of sampled RUM sessions that record a replay, from 0.0 to 100.0. There is no default: the SDK requires a value. Start at 30.0 and raise it once you know the volume you want.
textAndInputPrivacyLevel TextAndInputPrivacyLevel.maskAll How text and input widgets are recorded.
imagePrivacyLevel ImagePrivacyLevel.maskAll How images are recorded.
touchPrivacyLevel TouchPrivacyLevel.hide Whether user touches are recorded.
customEndpoint Not set Sends replay data to a self-hosted collector instead of the default endpoint.
Note: The replay sample rate applies to sessions that sessionSamplingRate already kept, so the two multiply. With the default session rate of 100, a replay sample rate of 30 records a replay for 30% of all sessions. At a session rate of 80, the same replay rate records 24%.

Privacy

Session Replay masks everything by default. Loosen the defaults only for the parts of your application you are sure carry no personal data.

Default privacy levels

TextAndInputPrivacyLevel controls text and input widgets:

Value What is recorded
maskAll All text and inputs are masked. This is the default.
maskAllInputs Text is recorded; all input widgets such as TextField, Checkbox, and Switch are masked.
maskSensitiveInputs Text and inputs are recorded, except inputs marked sensitive with EditableText.obscureText.

ImagePrivacyLevel controls images:

Value What is recorded
maskAll No images are recorded. This is the default.
maskNonAssetsOnly Only images bundled with the application as assets are recorded.
maskNone All images are recorded, including ones downloaded at runtime.

TouchPrivacyLevel controls touch indicators:

Value What is recorded
hide User touches are not shown. This is the default.
show User touches are shown in the replay.

Override privacy for part of the tree

Wrap a subtree in SessionReplayPrivacy to change the privacy level for that part of the application, or to hide it entirely:

copy
icon/buttons/copy
// Record this screen's text, but keep everything else masked
SessionReplayPrivacy(
  textAndInputPrivacyLevel: TextAndInputPrivacyLevel.maskSensitiveInputs,
  child: const ProductDetailScreen(),
);

// Hide a payment form completely
SessionReplayPrivacy(
  hide: true,
  child: const PaymentForm(),
);

Setting an option to null leaves that level unchanged for the subtree.

Important: A hidden subtree is replaced by a placeholder labelled "Hidden" in the replay and its children are never processed, so a widget deeper in the tree cannot be un-hidden. The same applies to TouchPrivacyLevel.hide: touch recording is cancelled for the whole subtree and cannot be re-enabled further down.

Validate That Replay Data Is Being Sent

To confirm the SDK is recording and uploading replays, raise its verbosity before you initialize it:

copy
icon/buttons/copy
AtatusSdk.instance.sdkVerbosity = CoreLoggerLevel.debug;

Verbosity defaults to CoreLoggerLevel.warn, and internal logging only runs in debug builds. Session Replay upload messages from the underlying Android SDK then appear in the device log.

Session Replay follows the same tracking consent value as the rest of the SDK, which you set at initialization and can update at any time. See Set Tracking Consent (GDPR).

Current Limitations

  • Recording cannot be started or stopped at runtime. Recording begins when Session Replay is enabled and continues for the session. Use replaySampleRate to control which sessions are recorded, or call enableSessionReplay conditionally to decide at startup.
  • Web view content is not captured in the replay. The recorder walks the Flutter widget tree, so anything a web view renders appears as an opaque region. RUM events from web views are still collected (use the atatus_webview_tracking package for that), but they do not produce replay footage.

Next steps