> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lucenthq.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Flutter support reference

> Reference for lucent_flutter configuration, replay behavior, persistence, and known limitations.

Use this page when you need the exact Flutter SDK behavior and option surface.
For setup instructions, start with the [Flutter SDK guide](/sdk/flutter).

## Support matrix

| Area                   | Flutter SDK behavior                                                                      |
| ---------------------- | ----------------------------------------------------------------------------------------- |
| Package                | `lucent_flutter`                                                                          |
| Supported platforms    | iOS and Android                                                                           |
| Deferred platforms     | Flutter Web and macOS are not in the MVP                                                  |
| Replay mode            | Screenshot-mode mobile replay                                                             |
| Backend classification | `lucent-flutter` maps to `mobile_flutter`                                                 |
| iOS packaging          | CocoaPods is verified                                                                     |
| Android packaging      | Flutter plugin with Android min SDK `23`; release apps need `android.permission.INTERNET` |
| Example app            | `sdk-flutter-lucent/example` in the SDK repo                                              |

## Replay envelope

Flutter replay is always sent as mobile screenshot replay:

```json theme={null}
{
  "snapshotSource": "mobile",
  "snapshotLibrary": "lucent-flutter",
  "snapshotMode": "screenshot"
}
```

The SDK captures a masked `RepaintBoundary` image from `LucentWidget`, encodes
it as a mobile screenshot wireframe, and posts batches to `/api/sdk/replay`.
The backend classifies these sessions as `mobile_flutter`.

## Persistence and retry

Replay batches are persisted on disk before upload on iOS and Android. Failed
uploads remain queued for retry.

| Limit                      | Value    |
| -------------------------- | -------- |
| Maximum queued batch files | `100`    |
| Maximum queued bytes       | `50 MB`  |
| Maximum queued age         | `7 days` |

Android also persists current in-memory replay events between flushes, so
active events can survive a process restart before they become an upload batch.

`flush()` is best effort. If the network request fails, the persisted batch
stays queued and is retried later.

When `LucentWidget` sees the app pause or become inactive, it emits
`Application Backgrounded` and requests a flush. When the app resumes, it emits
`Application Became Active`.

## Configuration

Pass `LucentConfig` to `Lucent().setup(...)`.

```dart theme={null}
await Lucent().setup(
  const LucentConfig(
    publicKey: 'luc_pk_...',
    ingestBaseUrl: 'https://ingest-api.lucenthq.com',
    environment: LucentEnvironment.production,
    autoStart: true,
    remoteConfig: true,
    sessionReplaySampleRate: 1,
    privacy: LucentPrivacyConfig(
      maskAllText: true,
      maskAllImages: true,
    ),
    capture: LucentCaptureConfig(
      touches: true,
      appLifecycle: true,
      errors: true,
      unhandledRejections: true,
      nativeCrashes: true,
      logs: true,
      network: true,
    ),
    batching: LucentBatchingConfig(
      flushInterval: Duration(seconds: 10),
      maxEventsPerBatch: 500,
      maxBatchBytes: 1048576,
    ),
    replay: LucentReplayConfig(
      minimumDuration: Duration.zero,
      screenshotThrottle: Duration(seconds: 1),
      screenshotBackgroundCapture: false,
      eventTriggers: [],
    ),
    metadata: LucentMetadataConfig(
      app: 'Acme Mobile',
      namespace: 'com.acme.mobile',
      version: '1.2.3',
      build: '123',
      release: '1.2.3+123',
      properties: {'tier': 'pro'},
    ),
  ),
);
```

### Core

| Option                    | Default                           | Notes                                                      |
| ------------------------- | --------------------------------- | ---------------------------------------------------------- |
| `publicKey`               | Required                          | Your Lucent public key. Safe to expose in client code.     |
| `ingestBaseUrl`           | `https://ingest-api.lucenthq.com` | Override only for self-hosted or internal testing.         |
| `environment`             | `LucentEnvironment.production`    | Use `staging` or `development` for non-production apps.    |
| `autoStart`               | `true`                            | Set to `false` when you need to wait for consent.          |
| `remoteConfig`            | `true`                            | Fetches `/api/sdk/init` before auto-starting when enabled. |
| `sessionReplaySampleRate` | `1`                               | Clamped between `0` and `1`.                               |

When `remoteConfig` is enabled, native replay waits for `/api/sdk/init` before
auto-starting. If the request fails, the SDK falls back to local config.

Remote config can disable recording, override session sample rate, update
batching limits, change `minimumDuration`, toggle log/network telemetry, and
replace `eventTriggers`.

### Privacy

| Option                  | Default | Notes                                                           |
| ----------------------- | ------- | --------------------------------------------------------------- |
| `privacy.maskAllText`   | `true`  | Masks `Text`, `RichText`, and editable text widgets by default. |
| `privacy.maskAllImages` | `true`  | Masks rendered image widgets by default.                        |

`LucentMask` forces masking for a subtree. `LucentNoMask` makes its subtree
visible, so keep sensitive widgets outside any `LucentNoMask` region.

<Warning>
  Flutter does not have browser-style CSS selectors. Review any
  `LucentNoMask` subtree carefully before shipping.
</Warning>

### Capture

| Option                        | Default | Captures                                                                           |
| ----------------------------- | ------- | ---------------------------------------------------------------------------------- |
| `capture.touches`             | `true`  | Pointer-down coordinates and `$touch` events.                                      |
| `capture.appLifecycle`        | `true`  | `Application Opened`, `Application Became Active`, and `Application Backgrounded`. |
| `capture.errors`              | `true`  | Flutter framework errors.                                                          |
| `capture.unhandledRejections` | `true`  | `PlatformDispatcher` async errors.                                                 |
| `capture.nativeCrashes`       | `true`  | Native uncaught exceptions on next launch.                                         |
| `capture.logs`                | `true`  | `debugPrint`, `print` inside `runZoned`, and manual logs.                          |
| `capture.network`             | `true`  | `dart:io` network breadcrumbs inside `runZoned` and manual network breadcrumbs.    |

### Batching

| Option                       | Default      | Notes                           |
| ---------------------------- | ------------ | ------------------------------- |
| `batching.flushInterval`     | `10 seconds` | Native flush loop interval.     |
| `batching.maxEventsPerBatch` | `500`        | Reaching this triggers a flush. |
| `batching.maxBatchBytes`     | `1048576`    | Reaching this triggers a flush. |

Native code clamps flush interval, event count, and byte limits to bounded
ranges so misconfiguration does not create extremely small or huge batches.

### Replay

| Option                               | Default         | Notes                                                            |
| ------------------------------------ | --------------- | ---------------------------------------------------------------- |
| `replay.minimumDuration`             | `Duration.zero` | Final flushes shorter than this are dropped.                     |
| `replay.screenshotThrottle`          | `1 second`      | How often `LucentWidget` attempts a screenshot.                  |
| `replay.screenshotBackgroundCapture` | `false`         | Screenshots stop while the app is backgrounded by default.       |
| `replay.eventTriggers`               | `[]`            | When set, recording starts after one of these events is tracked. |

`snapshotMode` is not configurable. It is always `screenshot`.

### Metadata and properties

| Option                | Event property             |
| --------------------- | -------------------------- |
| `metadata.app`        | `$app_name`                |
| `metadata.namespace`  | `$app_namespace`           |
| `metadata.version`    | `$app_version`             |
| `metadata.build`      | `$app_build`               |
| `metadata.release`    | `release`                  |
| `metadata.properties` | Merged into event metadata |

Use `Lucent().setSessionProperties(...)` and `Lucent().setUserProperties(...)`
when values change during a session.

The native payload also includes common mobile context: `userAgent`, `language`,
`snapshotSource`, `snapshotLibrary`, `snapshotMode`, `os`, `deviceType`, and
`environment`.

## Public API

| Method                                                       | Use for                                                                        |
| ------------------------------------------------------------ | ------------------------------------------------------------------------------ |
| `Lucent().setup(config)`                                     | Initialize the SDK and native modules.                                         |
| `Lucent().stop()`                                            | Stop native recording, uninstall Dart hooks, and perform a final native flush. |
| `Lucent().track(name, properties?)`                          | Capture a custom event.                                                        |
| `Lucent().identify(user)`                                    | Associate future events with a known user.                                     |
| `Lucent().resetIdentity()`                                   | Clear the known user and generate a new anonymous ID.                          |
| `Lucent().screen(name, properties?)`                         | Emit `$screen` and update replay screen context.                               |
| `Lucent().setSessionProperties(properties)`                  | Attach session-level properties.                                               |
| `Lucent().setUserProperties(properties)`                     | Attach user-level properties.                                                  |
| `Lucent().captureLog(message, level, properties)`            | Capture a manual log breadcrumb.                                               |
| `Lucent().captureNetwork(...)`                               | Capture a manual network breadcrumb.                                           |
| `Lucent().captureNetworkRequest(...)`                        | Wrap an async request and capture timing/status.                               |
| `Lucent().captureException(error, stackTrace?, properties?)` | Capture a handled exception.                                                   |
| `Lucent().addExceptionStep(message, properties?)`            | Add an exception breadcrumb.                                                   |
| `Lucent().startSessionRecording(resumeCurrent: true)`        | Resume screenshot replay.                                                      |
| `Lucent().stopSessionRecording()`                            | Pause screenshot replay and final flush.                                       |
| `Lucent().isSessionReplayActive()`                           | Check whether replay is currently active.                                      |
| `Lucent().flush(finalFlush: false)`                          | Flush queued events. Set `finalFlush: true` for a final flush.                 |
| `Lucent().getSessionInfo()`                                  | Read `sessionId`, `windowId`, and recording state.                             |
| `Lucent().runZoned(body, onError?)`                          | Capture `print`, async errors, and `dart:io` network inside a zone.            |

`getSessionInfo()` returns `sessionId`, `windowId`, and `recordingEnabled`.
Use `runZoned(..., onError: handler)` if your app already has its own
top-level async error handler.

## Navigation reference

`LucentObserver` accepts two optional callbacks:

| Callback                                | Use for                                                                    |
| --------------------------------------- | -------------------------------------------------------------------------- |
| `nameExtractor(RouteSettings settings)` | Convert route settings into a screen name. Defaults to `settings.name`.    |
| `routeFilter(Route<dynamic>? route)`    | Decide which routes should emit screens. Defaults to `route is PageRoute`. |

For routers that do not expose named `PageRoute`s, call `Lucent().screen(...)`
from your router listener instead.

## Network privacy

Automatic network capture is limited to `dart:io` `HttpClient` traffic created
inside `Lucent().runZoned(...)`. Other HTTP clients need a wrapper or
interceptor that calls `captureNetworkRequest` or `captureNetwork`.

Lucent captures:

| Field                              | Captured            |
| ---------------------------------- | ------------------- |
| HTTP method                        | Yes                 |
| URL origin and path                | Yes                 |
| URL user info, query, and fragment | No                  |
| Status code                        | Yes, when available |
| Duration                           | Yes, when available |
| Transfer size                      | Yes, when available |
| Request headers                    | No                  |
| Response headers                   | No                  |
| Request body                       | No                  |
| Response body                      | No                  |

Because headers and bodies are not captured, there are no per-field masking
rules for them. Sanitize URLs before manual capture if a path segment contains
sensitive data.

## Error and crash behavior

| Error source               | Behavior                                                                                              |
| -------------------------- | ----------------------------------------------------------------------------------------------------- |
| Flutter framework errors   | Captured through `FlutterError.onError` when `capture.errors` is enabled.                             |
| Async platform errors      | Captured through `PlatformDispatcher.instance.onError` when `capture.unhandledRejections` is enabled. |
| Handled exceptions         | Capture manually with `Lucent().captureException(...)`.                                               |
| Native uncaught exceptions | Stored synchronously and emitted as `$native_crash` on the next launch.                               |

Native crash delivery is best effort. If the process cannot write the pending
crash payload before termination, there may be no next-launch crash event.

## Parity checklist

| Capability                        | Flutter SDK | Notes                                                 |
| --------------------------------- | ----------- | ----------------------------------------------------- |
| iOS replay                        | Supported   | Screenshot-mode mobile replay.                        |
| Android replay                    | Supported   | Screenshot-mode mobile replay.                        |
| Flutter Web replay                | Deferred    | No web/canvas capture path in the MVP.                |
| Root replay wrapper               | Supported   | Use `LucentWidget`.                                   |
| Route tracking                    | Supported   | Use `LucentObserver` or `Lucent().screen(...)`.       |
| Custom events                     | Supported   | Use `Lucent().track(...)`.                            |
| Identify/reset                    | Supported   | Use `LucentUser` and `resetIdentity`.                 |
| Touch capture                     | Supported   | Pointer-down coordinate events.                       |
| Privacy defaults                  | Supported   | Text, inputs, and images mask by default.             |
| Logs                              | Supported   | `debugPrint`, `print` in `runZoned`, and manual logs. |
| Network breadcrumbs               | Supported   | `dart:io` in `runZoned` plus manual APIs.             |
| Handled errors                    | Supported   | `captureException` and exception steps.               |
| Native crashes                    | Supported   | Next-launch `$native_crash`, best effort.             |
| Persistent replay queue           | Supported   | iOS and Android batch queues with bounded retention.  |
| Android current-event persistence | Supported   | Current events persist between flushes.               |
| Remote config                     | Supported   | `/api/sdk/init` when enabled.                         |
| Event-triggered replay            | Supported   | `LucentReplayConfig.eventTriggers`.                   |
| CocoaPods                         | Supported   | Verified iOS packaging path.                          |
| Swift Package Manager             | Deferred    | Use CocoaPods for the MVP.                            |

## Not in the Flutter MVP

These browser or React Native capabilities are not available in
`lucent_flutter` 0.1.0:

| Capability                          | Status                                                                                       |
| ----------------------------------- | -------------------------------------------------------------------------------------------- |
| Flutter Web replay                  | Deferred until a web/canvas capture path exists.                                             |
| CSS selector privacy rules          | Not applicable to Flutter widget trees. Use `LucentMask` and `LucentNoMask`.                 |
| `networkIgnoreUrls`                 | Not exposed. Sanitize URLs manually before manual capture if needed.                         |
| Console level filtering             | Not exposed. `debugPrint`, zoned `print`, and manual logs use the configured capture toggle. |
| Rage-click and dead-click detection | Browser-only today. Flutter touch capture is coordinate-level.                               |
| Alias/group APIs                    | Not in the Flutter MVP.                                                                      |
| Persistent super-property registry  | Not in the Flutter MVP. Use metadata, session properties, and user properties.               |
| Source maps or native symbolication | External to the Flutter SDK docs today.                                                      |
| Expo/Metro tooling                  | React Native only.                                                                           |

## Troubleshooting

| Symptom                            | Check                                                                                                               |
| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| No replay appears                  | Confirm `LucentWidget` wraps the app root and `sessionReplaySampleRate` is greater than `0`.                        |
| No screen names                    | Add named routes, customize `LucentObserver`, or call `Lucent().screen(...)` manually.                              |
| `print` logs are missing           | Make sure `runApp` runs inside `Lucent().runZoned(...)`.                                                            |
| Network calls are missing          | Confirm the client uses `dart:io` inside `runZoned`, or add manual/interceptor capture.                             |
| iOS build fails around pods        | Run `flutter pub get`, then `cd ios && pod install`.                                                                |
| iOS SPM warnings appear            | Use CocoaPods for the MVP; Swift Package Manager support is deferred.                                               |
| A physical iOS device will not run | Enable Developer Mode and trust the development certificate/device pairing.                                         |
| Android release uploads fail       | Add `<uses-permission android:name="android.permission.INTERNET" />` to `android/app/src/main/AndroidManifest.xml`. |
| Android device install fails       | Confirm USB debugging is enabled and the device trusts the development machine.                                     |
| Replay is too sparse               | Lower `replay.screenshotThrottle`, but watch bandwidth and battery use.                                             |
