Skip to main content

Session Replay

Session Replay

Session Replay allows you to record and replay user sessions for debugging and analytics purposes.

Setup

First, initialize the Session Replay masking handler in your main() function:

import 'package:cx_flutter_plugin/cx_session_replay_masking.dart';

Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await SessionReplayMasking.initialize();

runZonedGuarded(() {
runApp(const MyApp());
}, (error, stackTrace) {
CxFlutterPlugin.reportError(error, {}, stackTrace.toString());
});
}

Initialize Session Replay

import 'package:cx_flutter_plugin/cx_session_replay_options.dart';

final options = CXSessionReplayOptions(
captureScale: 1.0, // Screenshot scale (0.0-1.0)
captureCompressQuality: 0.8, // JPEG compression quality (0.0-1.0)
sessionRecordingSampleRate: 100, // Percentage of sessions to record (0-100)
autoStartSessionRecording: true, // Start recording automatically
maskAllTexts: false, // Mask all text in screenshots
textsToMask: ['password', 'credit'], // Regex patterns — mask text containing these words
maskAllImages: false, // Mask all images in screenshots
);

// Masking for Flutter content is performed in DART on both iOS and Android: the
// native SDK requests a pre-masked bitmap of the Flutter view on each capture, and
// the plugin walks the render tree to black out text/images before handing the bytes
// to native. CXSessionReplayOptions is the single source of truth for masking —
// SessionReplayMasking.initialize() in main() only registers the handler and takes
// no masking arguments.
// textsToMask patterns are RegExp strings, case-sensitive by default ('password'
// will not match "Password"). Dart's RegExp has no inline (?i) flag — use a character
// class instead: '[Pp]assword'. Text fields (RenderEditable) are always masked while
// masking is active; their live content is not read, to avoid inspecting sensitive input.

await CxFlutterPlugin.initializeSessionReplay(options);

Masking

All masking is configured through CXSessionReplayOptions — that single object is the source of truth. SessionReplayMasking.initialize() in main() only registers the handler; it takes no masking arguments. To change masking later, just call initializeSessionReplay(newOptions) again.

Mask everything (text + images):

CXSessionReplayOptions(
maskAllTexts: true,
maskAllImages: true,
// ...other options
)

Mask only specific text (regex), leave the rest visible:

CXSessionReplayOptions(
maskAllTexts: false, // turn off "mask all text"
textsToMask: [r'\d{3}-\d{2}-\d{4}', // e.g. SSNs
'^Session.*', // text starting with "Session"
'password'], // RegExp strings, case-sensitive
maskAllImages: false,
)

Mask only images:

CXSessionReplayOptions(maskAllTexts: false, maskAllImages: true)

Mask one specific widget (regardless of the flags above) — wrap it in MaskedWidget:

import 'package:cx_flutter_plugin/cx_session_replay_masking.dart';

MaskedWidget(child: Image.network(avatarUrl))

// Conditional masking:
MaskedWidget(
isMasked: true, // set to false to temporarily disable
child: TextField(decoration: InputDecoration(labelText: 'Credit Card')),
)

Notes:

  • maskAllTexts and maskAllImages are independent — enable either or both.
  • maskAllImages covers Image.*, RawImage, and Container/CircleAvatar background images (DecorationImage). For content the render tree can't expose (platform views, native maps/WebViews), wrap it in MaskedWidget.
  • Icons (Icon, glyph fonts) are treated as neither text nor images and stay visible.

Check Status

// Check if Session Replay is initialized
final isInitialized = await CxFlutterPlugin.isSessionReplayInitialized();

// Check if currently recording
final isRecording = await CxFlutterPlugin.isRecording();

Control Recording

// Start recording
await CxFlutterPlugin.startSessionRecording();

// Stop recording
await CxFlutterPlugin.stopSessionRecording();

// Shutdown Session Replay
await CxFlutterPlugin.shutdownSessionReplay();

Capture Manual Screenshot

await CxFlutterPlugin.captureScreenshot();

Get Session Replay Folder Path

final folderPath = await CxFlutterPlugin.getSessionReplayFolderPath();

For more info check https://github.com/coralogix/cx-ios-sdk/tree/master/Coralogix/Docs.

Last updated on