This article covers the installation of Session Replay for Flutter.

{% callout type="warning" heading="iOS and Android only" %}
The Session Replay Flutter SDK supports iOS and Android only. The SDK doesn't support Flutter Web, macOS, Windows, or Linux.
{% /callout %}

{% callout type="warning" heading="Early Access" %}
Session Replay for Flutter is in Early Access. APIs may change and you should expect significant changes before the feature reaches General Availability.

To report issues with Session Replay for Flutter, contact [Amplitude support](https://gethelp.amplitude.com/hc/en-us/requests/new).
{% /callout %}

## Before you begin

Use the latest version of the [`amplitude_session_replay`](https://pub.dev/packages/amplitude_session_replay) package.

The Session Replay Flutter SDK requires that:

1. Your application runs on iOS 13.0+ or Android 5.0+ (minSdk 21).
2. Your project uses Dart SDK 3.7.2 or later and Flutter SDK 3.29.2 or later.
3. You can provide a `device ID` and `session ID` to the SDK. These values must match the identifiers you send as event properties to Amplitude.

The SDK doesn't provide session management. Your application or a third-party integration must update the SDK when the session ID or device ID changes.

### 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 |

## Quickstart

Add Session Replay to your `pubspec.yaml`:

```yaml
dependencies:
  amplitude_session_replay: ^0.1.0-beta.1
```

Then run `flutter pub get`.

Configure your application code:

1. Create a `SessionReplay` instance with `SessionReplayConfig`, passing your API key, device ID, and session ID.
2. Call `start()` to initialize the native SDK and begin recording.
3. Wrap your app with `SessionReplayWidget` so the recording engine has a Flutter render context.
4. When the session ID or device ID changes, call `setSessionId()` or `setDeviceId()` to keep Session Replay synchronized.

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

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  final sessionReplay = SessionReplay(
    SessionReplayConfig(
      apiKey: 'YOUR_AMPLITUDE_API_KEY',
      deviceId: 'your-device-id',
      sessionId: DateTime.now().millisecondsSinceEpoch,
      sampleRate: 0.1,
    ),
  );

  await sessionReplay.start();

  runApp(SessionReplayWidget(sessionReplay: sessionReplay, app: const MyApp()));
}
```


## Configuration

Pass the following options through `SessionReplayConfig` when you create the `SessionReplay` instance:

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `apiKey` | `String` | Yes | — | Your Amplitude API key for authentication and data routing. |
| `deviceId` | `String` | Yes | — | Device identifier. Must match the device ID you send with Amplitude events. Must be at least 6 characters. |
| `sessionId` | `int` | No | `-1` | Session identifier in milliseconds since epoch. A value of `-1` means no active session, and Session Replay doesn't record. Must match the session ID you send with Amplitude events. |
| `sampleRate` | `double` | No | `0.0` | Fraction of sessions to capture for replay (0.0–1.0). For example, `0.4` captures 40% of 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` | When `true`, Session Replay doesn't record or upload data. Use `setOptOut()` to change at runtime. |
| `serverZone` | `ServerZone` | No | `ServerZone.us` | Server zone for data residency. Set to `ServerZone.eu` for EU data center. |
| `shouldInitializeNativeSDK` | `bool` | No | `true` | When `false`, the Flutter SDK skips native SDK initialization. Use this for [hybrid and add-to-app](#hybrid-and-add-to-app) setups where a native host app already initializes the Amplitude Session Replay native SDK. |

{% partial file="session-replay/sr-remote-config-test.md" /%}

### 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

Replaces every character of captured text with an asterisk (`*`), preserving the original text length and layout. Use this to protect sensitive information that the automatic privacy level doesn't catch:

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

#### 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'),
)
```

### User opt-out

To opt users out of session replay collection, pass `optOut: true` during initialization, or call `setOptOut(optOut: true)` at runtime. Opted-out users don't record or upload replay data.

```dart
await sessionReplay.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
final sessionReplay = SessionReplay(
  SessionReplayConfig(
    apiKey: 'YOUR_AMPLITUDE_API_KEY',
    deviceId: 'your-device-id',
    sessionId: DateTime.now().millisecondsSinceEpoch,
    serverZone: ServerZone.eu,
  ),
);
```

{% partial file="session-replay/sr-sampling-rate.md" /%}

```dart
final sessionReplay = SessionReplay(
  SessionReplayConfig(
    apiKey: 'YOUR_AMPLITUDE_API_KEY',
    deviceId: 'your-device-id',
    sessionId: DateTime.now().millisecondsSinceEpoch,
    sampleRate: 0.01, // Capture 1% of sessions
  ),
);
```

### Update the session ID

When your session ID changes (for example, on user login or session timeout), update Session Replay:

```dart
final newSessionId = DateTime.now().millisecondsSinceEpoch;
await sessionReplay.setSessionId(newSessionId);
```

### Start and stop recording

Control recording for specific pages or features:

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

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

### Disable replay collection

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

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

Call `sessionReplay.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.start()` to re-enable replay collection when the user returns to an unrestricted area of your app.

### Hybrid and add-to-app

If your Flutter module runs inside a native iOS or Android host that already integrates the Amplitude Session Replay native SDK, set `shouldInitializeNativeSDK: false` in `SessionReplayConfig`. This tells the Flutter SDK to skip native SDK initialization and let the host app's native SDK own the API key, device ID, session ID, sampling, and upload lifecycle. You still need `SessionReplayWidget` to enable recording.

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

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  final sessionReplay = SessionReplay(
    SessionReplayConfig(
      apiKey: 'YOUR_AMPLITUDE_API_KEY',
      deviceId: 'your-device-id',
      sessionId: DateTime.now().millisecondsSinceEpoch,
      shouldInitializeNativeSDK: false,
    ),
  );

  await sessionReplay.start();

  runApp(SessionReplayWidget(sessionReplay: sessionReplay, app: const MyApp()));
}
```

## Methods

The `SessionReplay` class exposes the following methods:

| Method | Returns | Description |
| --- | --- | --- |
| `start()` | `Future<void>` | Start recording. Initializes the native SDK on first call. |
| `stop()` | `Future<void>` | Stop recording. Call `start()` to resume. |
| `dispose()` | `Future<void>` | Release all resources. The instance can't be reused after disposal. |
| `flush()` | `Future<void>` | Force-upload any pending replay data. Useful before the app backgrounds or terminates. |
| `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>` | Toggle opt-out at runtime. When `true`, stops recording and data upload. |
| `optOut` (getter) | `bool` | Get the current opt-out status. |

### Lifecycle

```
Created → start() → Started → stop() → Stopped → start() → Started ...
                                              ↘ dispose() → Disposed
```

You can call `dispose()` from any state. It's idempotent — calling it on an already-disposed instance is a no-op. Use `dispose()` for permanent teardown only. For temporarily pausing and resuming recording, use `stop()` and `start()` instead.

Once disposed, the instance can't be reused. To resume recording after disposal, create a new `SessionReplay` instance **and** remount `SessionReplayWidget` with the new instance (for example, by assigning a new `Key` to force a rebuild). The engine is bound to the widget at mount time and doesn't rebind when `SessionReplay` is replaced.

{% partial file="session-replay/sr-data-retention.md" /%}

## Known limitations

1. **Beta status**: APIs may change. Expect breaking changes before the stable release.
2. **RSuperellipse capture**: Requires Flutter 3.32 or later. Base functionality works on Flutter 3.29.2+.
3. **Multi-view apps**: The SDK captures only the first attached `FlutterView`. The SDK doesn't support `runWidget`, `FlutterEngineGroup`, or view remounts after `start()`.
4. **Remote config override**: When `enableRemoteConfig` is `true`, server settings can override the local `sampleRate` and privacy settings.
5. **Native SDK dependencies**: iOS uses `AmplitudeSessionReplay` ~>0.10.0. Android uses `session-replay-android` [0.26.0, 0.27.0).

## 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.
- `sessionReplay.start()` wasn't called after construction.

#### Verify your configuration

1. Check that `sampleRate` is greater than `0`. The default is `0.0`, which captures no sessions.
2. Confirm you call `sessionReplay.start()` after constructing the `SessionReplay` instance.
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

The default `sampleRate` is `0.0`. Update the rate to a higher number. For more information, 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.
