---
title: Flutter Session Replay
description: Record Flutter application sessions on iOS and Android and connect replays to your Amplitude analytics events.
product: session-replay
lang: en
last_updated: 2026-10-06
token_estimate: 5925
---
# Flutter Session Replay

> For AI agents: a documentation index is available at [/docs/llms.txt](/docs/llms.txt). Append `.md` to any page URL for markdown, or send `Accept: text/markdown`.

- **Package:** `amplitude_session_replay` ([pubdev](https://pub.dev/packages/amplitude_session_replay))
- **Latest version:** 1.0.1

Flutter Session Replay records application sessions on iOS and Android. Connect replays to your Amplitude events by sharing device and session IDs. Use [add-to-app](#add-to-app) when an iOS or Android host app manages recording.

Use the current stable release, `1.0.1`, of the [`amplitude_session_replay`](https://pub.dev/packages/amplitude_session_replay) package. If you're upgrading from a beta, refer to [Upgrade from earlier beta versions](#upgrade-from-earlier-beta-versions).

You need your Amplitude project's API key to initialize Flutter Session Replay. You must also set a device ID and an active session ID. To link replays with analytics events, use the same top-level `device_id` and `session_id` values. Follow [Connect Session Replay](#connect-session-replay) to use the IDs from Analytics Flutter.

Keep these IDs synchronized when your application starts a new session or changes identity. To report issues, contact [Amplitude support](https://gethelp.amplitude.com/hc/en-us/requests/new).

## Compatibility

| Requirement | Minimum version |
| --- | --- |
| Dart SDK | 3.7.2 |
| Flutter SDK | 3.29.2 |
| iOS | 13.0 |
| Android minSdk | 21 (Android 5.0) |
| Android compileSdk | 36 |

Widgets that use `RSuperellipse` require Flutter 3.32 or later. Flutter-only recording captures the primary Flutter view; for multiple-engine host applications, follow [Add-to-app](#add-to-app).

## Quickstart

### Configure your Amplitude project

In [Session Replay settings](https://amplitude.com/docs/session-replay/session-replay-settings), enable session capture for your project and set its sampling rate. Configure its masking level using [Manage privacy settings](https://amplitude.com/docs/session-replay/manage-privacy-settings-for-session-replay#masking-levels).

By default, Flutter Session Replay reads sampling and masking settings from the Amplitude project identified by your `apiKey`. Set that project's sampling rate above 0% before starting recording.

### Install and initialize

Add Flutter Session Replay to your `pubspec.yaml`:

```yaml
dependencies:
  amplitude_session_replay: ^1.0.1
```

Then run `flutter pub get`.

`SessionReplay` is a process-wide singleton. Access it through `SessionReplay.instance`, and don't construct it directly.

Configure your application code:

1. Call `SessionReplay.instance.init()` with a `SessionReplayConfig`, passing your API key, device ID, and session ID. `init()` is synchronous.
2. Call `start()` to request recording.
3. When the session ID or device ID changes, call `setSessionId()` or `setDeviceId()` to keep Session Replay synchronized.

> **Note:** Analytics identifiers
>
> The examples use an initialized [Analytics Flutter client](https://amplitude.com/docs/sdks/analytics/flutter/flutter-sdk-4#initialize-the-sdk) named `analytics`. Read its device and session IDs before configuring Replay. Later configuration examples reuse the validated `deviceId` and `sessionId` from this setup. If you use another analytics provider, supply that provider's matching IDs.

```dart
import 'package:amplitude_flutter/amplitude.dart';
import 'package:amplitude_session_replay/amplitude_session_replay.dart';

Future<void> startReplay(Amplitude analytics) async {
  await analytics.isBuilt;
  final deviceId = await analytics.getDeviceId();
  final sessionId = await analytics.getSessionId();
  if (deviceId == null || sessionId == null || sessionId <= 0) return;

  SessionReplay.instance.init(
    SessionReplayConfig(
      apiKey: 'YOUR_AMPLITUDE_API_KEY',
      deviceId: deviceId,
      sessionId: sessionId,
      privacyConfig: PrivacyConfig.conservative,
    ),
  );
  await SessionReplay.instance.start();
}
```

### Debug recording

> **Note:** Debug sessions only
>
> For local debugging, disable remote configuration and set `sampleRate: 1.0` to select every test session. Use the same development project for Analytics and Replay, and keep the required masking level.

Supply a production configuration with the same project API key, server zone, and current device and session IDs as the Analytics client.

```dart
import 'package:amplitude_session_replay/amplitude_session_replay.dart';
import 'package:flutter/foundation.dart';

Future<void> startReplayForBuild(
  SessionReplayConfig productionConfig,
) async {
  final config = kDebugMode
      ? SessionReplayConfig(
          apiKey: 'YOUR_DEVELOPMENT_AMPLITUDE_API_KEY',
          deviceId: productionConfig.deviceId,
          sessionId: productionConfig.sessionId,
          enableRemoteConfig: false,
          sampleRate: 1.0,
          privacyConfig: PrivacyConfig.conservative,
        )
      : productionConfig;

  SessionReplay.instance.init(config);
  await SessionReplay.instance.start();
}
```

## Connect Session Replay

To link replays with analytics events, use the same project API key, server zone, device ID, and session ID for both SDKs. Refer to [Session matching](https://amplitude.com/docs/session-replay/session-matching).

Use the current [Analytics Flutter SDK](https://amplitude.com/docs/sdks/analytics/flutter/flutter-sdk-4#initialize-the-sdk) and read its [session ID](https://amplitude.com/docs/sdks/analytics/flutter/flutter-sdk-4#track-sessions) and [device ID](https://amplitude.com/docs/sdks/analytics/flutter/flutter-sdk-4#custom-device-id) with `getSessionId()` and `getDeviceId()`. Update Replay's IDs when Analytics changes them. Stop recording during identity changes and resume after the IDs match. Apply consent changes to both SDKs.

For applications that embed Flutter in an iOS or Android host, follow [Add-to-app](#add-to-app).

## Configuration

Pass the following options through `SessionReplayConfig` when you call `SessionReplay.instance.init()`:

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `apiKey` | `String` | Yes | — | Your Amplitude API key for authentication and data routing. |
| `deviceId` | `String` | Yes | — | Device identifier of at least 6 characters. To link replays with analytics events, use the same device ID. |
| `sessionId` | `int` | No | `-1` | Session identifier in milliseconds since epoch. A value of `-1` means no active session, and Session Replay doesn't record. To link replays with analytics events, use the same session ID. |
| `sampleRate` | `double` | No | `0.0` | Local fraction of sessions to capture (0.0–1.0). Project remote settings take precedence. For example, `0.4` selects 40% of sessions when remote configuration is disabled. The local default selects no sessions. |
| `logLevel` | `LogLevel` | No | `LogLevel.warn` | Logging verbosity. Options: `LogLevel.off`, `LogLevel.error`, `LogLevel.warn`, `LogLevel.log`, `LogLevel.debug`. |
| `privacyConfig` | `PrivacyConfig` | No | `PrivacyConfig.medium` | Automatic masking behavior. Options: `PrivacyConfig.conservative`, `PrivacyConfig.medium`, `PrivacyConfig.light`. |
| `enableRemoteConfig` | `bool` | No | `true` | Enables [remote configuration](#remote-configuration) from Amplitude servers. |
| `optOut` | `bool` | No | `false` | Opt users out of recording and uploads. Use `setOptOut()` to change at runtime. |
| `serverZone` | `ServerZone` | No | `ServerZone.us` | Server zone for data residency. Set to `ServerZone.eu` for EU data center. |

### Remote configuration

With `enableRemoteConfig: true`, the project's remote settings take precedence over local sampling and privacy settings. Manage those settings in [Session Replay settings](https://amplitude.com/docs/session-replay/session-replay-settings).

The default local privacy level is `PrivacyConfig.medium`. Set the required `privacyConfig` before starting recording, and use privacy wrappers for sensitive content. Local masking applies until the project's remote privacy setting arrives, so the local level must protect content from the start.

For local testing, follow [Debug recording](#debug-recording).

### Mask onscreen data

Session Replay provides three Flutter widgets for privacy control. These widgets apply to the entire subtree of the wrapped widget. Priority order: `AmpBlock` > `AmpMask` > `AmpUnmask`.

#### Privacy levels

The `privacyConfig` option controls automatic masking behavior:

| Level | Behavior |
| --- | --- |
| `PrivacyConfig.conservative` | Masks all text and all form fields. |
| `PrivacyConfig.medium` (default) | Masks all form fields and text inputs. |
| `PrivacyConfig.light` | Masks only password fields. |

#### AmpMask

Masks Flutter text with a rectangle matching the text bounds. Use `AmpMask` for sensitive text that the automatic privacy level doesn't cover:

```dart
AmpMask(
  child: Text('Sensitive information'),
)
```

Images don't receive automatic masking at any privacy level. Wrap sensitive images or image-containing subtrees in `AmpBlock`. `AmpMask` doesn't hide image content.

#### AmpBlock

Blocks an entire subtree from recording and replaces it with a placeholder. Use this for highly sensitive content:

```dart
AmpBlock(
  child: TextField(
    decoration: InputDecoration(labelText: 'Password'),
  ),
)
```

#### AmpUnmask

Prevents automatic masking from privacy level rules. Use this to reveal content that the privacy level would otherwise mask. `AmpUnmask` can't override a manual `AmpMask` or `AmpBlock`:

```dart
AmpUnmask(
  child: Text('Public content'),
)
```

#### Platform views

Supported embedded platform views are blocked by default, including views without a Flutter privacy wrapper. Register the plugin before creating these views. The application UI stays visible. Use `AmpUnmask` only when the platform view and every descendant are safe to record. Wrapper precedence remains `AmpBlock` > `AmpMask` > `AmpUnmask`.

| Wrapper | Android platform views | iOS platform views |
| --- | --- | --- |
| `AmpMask` | Masks native text with asterisks. | Blocks the view with a gray placeholder. |
| `AmpBlock` | Blocks the view with a placeholder. | Blocks the view with a gray placeholder. |
| `AmpUnmask` | Permits recording of the view and its descendants. | Permits recording of the view and its descendants. |

To record maps, use Android Google Maps or iOS MapKit and wrap the view in `AmpUnmask`.

Keep sensitive platform views blocked from creation. Later masking can retain previously captured pixels. On Android, keep sensitive WebViews blocked rather than switching them to `AmpMask` after recording begins.

Platform views can appear above Flutter overlays in a replay. For installation requirements, refer to [Plugin registration](#plugin-registration).

### User opt-out

To opt users out of session replay collection, pass `optOut: true` during initialization, or call `setOptOut(optOut: true)` at runtime. Apply consent changes to both Replay and your analytics SDK. Stop capture before displaying restricted content.

```dart
await SessionReplay.instance.setOptOut(optOut: true);
```

### EU data residency

Amplitude customers who use the EU data center can access Session Replay. Set `serverZone` to `ServerZone.eu` during initialization.

```dart
SessionReplay.instance.init(
  SessionReplayConfig(
    apiKey: 'YOUR_AMPLITUDE_API_KEY',
    deviceId: deviceId,
    sessionId: sessionId,
    serverZone: ServerZone.eu,
  ),
);
```

### Sampling rate

Set the project's sampling rate in [Session Replay settings](https://amplitude.com/docs/session-replay/session-replay-settings#sampling). Remote configuration is enabled by default and takes precedence over the local `sampleRate`.

For local testing, follow [Debug recording](#debug-recording) to disable remote configuration and select every test session.

### Update the session ID

When Analytics starts a new session, read its current session ID and pass it to Flutter Session Replay. In this example, `analytics` is your initialized Analytics Flutter client:

```dart
final sessionId = await analytics.getSessionId();
if (sessionId != null && sessionId > 0) {
  await SessionReplay.instance.setSessionId(sessionId);
}
```

If your application manages session IDs, pass the same updated ID to Replay and your analytics events. Refer to [Connect Session Replay](#connect-session-replay) for identity and session synchronization.

### Start and stop recording

Control recording for specific pages or features:

```dart
// Stop recording before entering a restricted area
await SessionReplay.instance.stop();

// Resume recording after leaving the restricted area
await SessionReplay.instance.start();
```

### Disable replay collection

After you enable Session Replay, it runs on your app until either:

- The user leaves your app.
- You call `SessionReplay.instance.stop()`.
- You call `SessionReplay.instance.dispose()`.

Call `SessionReplay.instance.stop()` before a user navigates to a restricted area of your app to disable replay collection while the user is in that area.

Call `SessionReplay.instance.start()` to re-enable replay collection when the user returns to an unrestricted area of your app.

### Add-to-app

If your Flutter module runs inside an iOS or Android host app that already records Session Replay, call `SessionReplay.instance.attach()` instead of `init()`. Configure the API key, device ID, session ID, sampling, masking, and recording lifecycle in the host app.

```dart
import 'package:amplitude_session_replay/amplitude_session_replay.dart';
import 'package:flutter/widgets.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();

  // Connect Flutter to the host app's Session Replay recording.
  SessionReplay.instance.attach();

  runApp(const MyApp());
}
```

In add-to-app integrations, configure recording through the host app. Dart `start()`, `stop()`, `flush()`, `setOptOut()`, `setSessionId()`, and `setDeviceId()` have no effect. Dart `dispose()` disconnects Flutter without stopping the host's recording.

If your app runs multiple Flutter engines, call `attach()` in each entry point.

#### Plugin registration

Automatic Flutter plugin registration handles this setup for standard Flutter applications. For manual registration or add-to-app integrations, register `AmplitudeSessionReplayPlugin` with every Flutter engine before creating platform views and before recording starts.

In add-to-app integrations, call `attach()` in each Flutter entry point in addition to registering the plugin. Registering after view creation can leave those views unblocked. In add-to-app integrations, check the plugin's `isNativeViewBlockingAvailable` property before the host starts recording.

## Methods

Call these methods on the `SessionReplay.instance` singleton:

| Method | Returns | Description |
| --- | --- | --- |
| `init(SessionReplayConfig)` | `void` | Configure Flutter Session Replay. Synchronous and one-shot per lifecycle. Call `dispose()` before re-initializing with a new config. |
| `attach()` | `void` | Connect Flutter to recording managed by an iOS or Android host app. Use instead of `init()`. |
| `start()` | `Future<void>` | Request recording. |
| `stop()` | `Future<void>` | Stop recording. Call `start()` to resume. |
| `dispose()` | `Future<void>` | Release all resources and return the singleton to its uninitialized state. Call `init()` or `attach()` again to reuse it. |
| `flush()` | `Future<void>` | Request upload of pending replay data after `start()`. Check logs for upload errors. |
| `setSessionId(int)` | `Future<void>` | Update the session ID. |
| `sessionId()` | `Future<int>` | Get the current session ID. |
| `setDeviceId(String)` | `Future<void>` | Update the device ID. |
| `deviceId()` | `Future<String>` | Get the current device ID. |
| `setOptOut(optOut: bool)` | `Future<void>` | Request opt-out at runtime. Apply consent to your analytics SDK separately. |
| `optOut` (getter) | `bool` | Get the current opt-out status. |

### Lifecycle

![Flutter Session Replay lifecycle: init(config) creates the Initialized state, start() moves to Started, stop() moves to Stopped, start() returns Stopped to Started, dispose() from any state moves to Disposed, and init(config) revives Disposed back to Initialized.](https://amplitude.com/docs/images/diagrams/flutter-session-replay-lifecycle.svg) ![Flutter Session Replay lifecycle: init(config) creates the Initialized state, start() moves to Started, stop() moves to Stopped, start() returns Stopped to Started, dispose() from any state moves to Disposed, and init(config) revives Disposed back to Initialized.](https://amplitude.com/docs/images/diagrams/flutter-session-replay-lifecycle.dark.svg)

`init()` is one-shot per lifecycle: it's only callable from the uninitialized (or disposed) state. To re-initialize with a new config, call `dispose()` first, then `init()` again.

You can call `dispose()` from any state. It returns the singleton to its uninitialized state, so a later `init()` or `attach()` cleanly revives it. Use `dispose()` for permanent teardown only. To temporarily pause and resume recording, use `stop()` and `start()` instead.

## Upgrade from earlier beta versions

Version `0.1.0-beta.5` introduced the singleton API. `SessionReplay` is a process-wide singleton, and installation no longer requires the `SessionReplayWidget` wrapper.

Experimental configuration options have changed. If your integration uses experimental options, contact [Amplitude support](https://gethelp.amplitude.com/hc/en-us/requests/new) for help migrating.

### Breaking changes

For Flutter-only apps:

| Old API | New API |
| --- | --- |
| `SessionReplay(config)` constructor | `SessionReplay.instance.init(config)` |
| `SessionReplayWidget(sessionReplay: ..., app: ...)` | Removed. No widget wrapping needed |
| `sessionReplayProperties()` | Removed. Replays match by `deviceId` and `sessionId` |

For add-to-app:

| Old API | New API |
| --- | --- |
| `SessionReplayConfig.shouldInitializeNativeSDK: false` | Removed. Call `SessionReplay.instance.attach()` |
| `ensureInitialized()` | `SessionReplay.instance.attach()` |

### Flutter-only apps

Replace the constructor and `SessionReplayWidget` wrapper with `SessionReplay.instance.init()` and `start()`:

```dart
// Before
final sessionReplay = SessionReplay(
  SessionReplayConfig(apiKey: '...', sampleRate: 1.0),
);
runApp(SessionReplayWidget(sessionReplay: sessionReplay, app: const MyApp()));
await sessionReplay.start();

// After
SessionReplay.instance.init(          // synchronous, no await
  SessionReplayConfig(
    apiKey: 'YOUR_AMPLITUDE_API_KEY',
    deviceId: deviceId,
    sessionId: sessionId,
  ),
);
await SessionReplay.instance.start();
runApp(const MyApp());                // no SessionReplayWidget wrapping needed
```

- `init()` is one-shot per lifecycle. Call `await SessionReplay.instance.dispose()` before re-initializing with a new config.
- Setters (`setSessionId()`, `setDeviceId()`, `setOptOut()`) still work between `init()` and `start()`.

### Add-to-app

Replace the placeholder config and widget wrapper with a single `attach()` call:

```dart
// Before: placeholder config and widget wrapper
return SessionReplayWidget(
  sessionReplay: SessionReplay(
    const SessionReplayConfig(
      apiKey: 'YOUR_API_KEY',
      deviceId: 'YOUR_DEVICE_ID',
      shouldInitializeNativeSDK: false,
    ),
  ),
  app: MaterialApp(...),
);

// After: one line per entry point, no config needed
void main() {
  WidgetsFlutterBinding.ensureInitialized();
  SessionReplay.instance.attach(); // host manages config and lifecycle
  runApp(const MyApp());
}
```

- Call `attach()` in every Dart entry point.
- Remove conflicting dependency version pins in the host app so the Flutter package can install its required dependencies.

### Session Replay properties

Remove all calls to `sessionReplayProperties()`. Amplitude matches replays to analytics events by `deviceId` and `sessionId`. Make sure those match the identifiers you send with your analytics events. For more information, refer to [Session matching](https://amplitude.com/docs/session-replay/session-matching).

The `AmpMask`, `AmpUnmask`, and `AmpBlock` privacy widgets are unchanged.

## Data retention, deletion, and privacy

Review [consent guidance](https://amplitude.com/docs/session-replay/best-practices-for-managing-user-consent) when you implement capture.

### Retention period

For native and Flutter SDK retention guidance and the effective date of changes, go to the [Session Replay data lifecycle reference](https://amplitude.com/docs/session-replay/replay-data-lifecycle#native-and-flutter-sdk-retention).

### DSAR API

For replay metadata returned by the DSAR API, go to [Session Replay DSAR metadata](https://amplitude.com/docs/session-replay/replay-data-lifecycle#dsar-api).

### Data deletion

For user and project deletion behavior, go to [Session Replay data deletion](https://amplitude.com/docs/session-replay/replay-data-lifecycle#data-deletion).

### Bot filter

For bot filtering and property-filter restrictions, go to [Session Replay bot filtering](https://amplitude.com/docs/session-replay/replay-data-lifecycle#bot-filter).

## Troubleshooting

### Session replays don't appear in Amplitude

Session replays may not appear because of:

- Lack of network connectivity.
- Sampling excluded the session (`sampleRate` too low).
- No events sent with matching `deviceId` and `sessionId` for the session.
- A missing `SessionReplay.instance.start()` call after `init()`.

#### Verify your configuration

1. Check that your project sampling rate is greater than 0%. If you disabled remote configuration, set the local `sampleRate` above `0.0`.
2. Confirm you call `SessionReplay.instance.start()` after `SessionReplay.instance.init()`.
3. Ensure the `apiKey`, `deviceId`, and `sessionId` match the values you send with analytics events.

#### Verify network connectivity

Ensure your app has access to the internet and try again.

#### Check sample rate

When remote configuration is enabled, check the sampling rate in [Session Replay settings](https://amplitude.com/docs/session-replay/session-replay-settings#sampling). For local configuration, set `sampleRate` above its `0.0` default. Refer to [Sampling rate](#sampling-rate).

### Session Replay processing errors

Replays appear in Amplitude within minutes of ingestion. Delays or errors may result from:

- Mismatched API keys or device IDs between Session Replay and your analytics instrumentation.
- Session Replay references the wrong project.
- Short sessions. If a user bounces within a few seconds of initialization, the SDK may not have time to upload replay data.

### Platform view blocking is unavailable

If recording stops with `NATIVE_VIEW_BLOCKING_UNAVAILABLE`, check that `AmplitudeSessionReplayPlugin` registers with every engine before platform-view creation. Confirm your Flutter version meets the compatibility requirements and remove conflicting dependency version pins.

Keep recording disabled until blocking is available. If the issue continues, contact [Amplitude support](https://gethelp.amplitude.com/hc/en-us/requests/new) with your Flutter version and logs. In add-to-app integrations, check the plugin's `isNativeViewBlockingAvailable` property before the host starts recording.

### Flush returns but no replay appears

Call `flush()` after `start()` to request upload of pending replay data. Check logs, connectivity, sampling, and matching analytics events if the replay doesn't appear. In add-to-app integrations, request the upload through the host app.

