cxorbi_flutter 1.4.0 copy "cxorbi_flutter: ^1.4.0" to clipboard
cxorbi_flutter: ^1.4.0 copied to clipboard

Cxorbi analytics for Flutter: session replay, heatmaps, screens, gestures, funnels, performance, error capture and in-app surveys for iOS and Android.

Cxorbi Flutter SDK #

Session replay, heatmaps, journeys, performance and error analytics for Flutter apps — iOS and Android from a single integration.

The SDK captures wireframe replays (no screenshots ever leave the device), gestures with target elements, screen views, app performance and errors. Image and custom-render assets are placeholders by default and can be uploaded only through explicit opt-in privacy settings. Records use the industry-standard two-dimension identity model:

  • platform — the host OS the session ran on: ios or android
  • framework — always flutter

So a Flutter session shows up under iOS or Android in the dashboard with a Flutter badge, exactly like React Native sessions do.

📚 Full documentation lives in the Docs section of your Cxorbi dashboard and at cxorbi.com — getting started, screen tracking, identity, events, transactions, session replay, heatmaps, customer journeys, funnels, error analysis, privacy & masking, API reference, data collection, performance impact, production checklist, compatibility, troubleshooting.

Requirements #

  • Flutter 3.27+ / Dart 3.5+
  • iOS and Android (web/desktop are not captured)
  • Works with flutter build --obfuscate — all widget detection uses compile-time type checks, never runtimeType strings

Installation #

flutter pub add cxorbi_flutter

No manual native (Pod/Gradle) setup is required for the default integration — standard Flutter dependency resolution wires the iOS/Android plugin.

Quick start #

For production apps, gate optIn() behind your consent/legal-basis flow. This quick start opts in immediately so you can verify the integration in a development or staging source.

import 'package:cxorbi_flutter/cxorbi_flutter.dart';
import 'package:flutter/material.dart';

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

  await Cxorbi.init(CxorbiConfig(
    apiKey: const String.fromEnvironment('CXORBI_SOURCE_TOKEN'),
    environment: CxorbiEnvironment.development,
    debugMode: true,
    logLevel: LogLevel.debug,
  ));

  // The SDK is opted OUT by default — nothing is captured or sent until
  // optIn() is called. In production, call this from your consent flow.
  await Cxorbi.instance.optIn();

  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      navigatorObservers: [CxorbiNavigatorObserver()], // automatic screen tracking
      // ...
    );
  }
}

AI agent setup #

This package ships a Flutter Agent Skill (skills/cxorbi_flutter-integrate). With an agent that supports the standard (Claude, Cursor, Copilot, …), install it from your dependencies and let the assistant do the wiring:

dart pub global activate skills   # once
skills get                        # installs cxorbi_flutter's skill into your IDE

Then prompt: "Integrate Cxorbi in this application." The skill is consent-gated by design — it never auto opts-in users and never fabricates an API key or user id.

Verify your integration #

  1. Run the app — the console prints Cxorbi Flutter SDK 1.3.1 starting — platform: ios, framework: flutter, then Cxorbi Flutter SDK consent accepted — session: …; waiting for first screenview.

  2. Navigate to the first named screen. Replay/error capture starts at the first screenview.

  3. Emit a deterministic test event after consent:

    Cxorbi.instance.track('cxorbi_integration_test', {
      'source': 'flutter_readme',
    });
    
  4. Open Live Events in the dashboard and confirm the screen view, taps, and cxorbi_integration_test event arrive within seconds.

  5. Open Session Replay — the session appears within a minute under the iOS/Android tab with a Flutter badge. Screens appear in Heatmaps → iOS/Android once a screen has interactions.

Before shipping, run the release-readiness checks in docs/production-checklist.md. Dashboard setup and copy-paste integration patterns live in docs/dashboard-onboarding.md and docs/reference-integrations.md.

Screen tracking #

CxorbiNavigatorObserver tracks every named route pushed, popped or replaced. Unnamed routes are ignored rather than guessed — give your routes names (RouteSettings(name: '/checkout')) or use the callbacks below.

MaterialApp(
  navigatorObservers: [
    CxorbiNavigatorObserver(
      // Skip dialogs / splash screens:
      excludeRoute: (route) => route.settings.name == '/splash',
      // Rename or derive names dynamically (return null = use route name):
      screenNameProvider: (route) =>
          route.settings.name == '/p' ? '/product-detail' : null,
      // Attach route-level variables:
      customVarsProvider: (route) => const [
        CustomVar(index: 1, name: 'area', value: 'checkout'),
      ],
    ),
  ],
)

Manual #

Call Cxorbi.instance.screen(name) whenever a screen becomes visible. Manual calls always win over the observer. You need manual calls for:

  • PageView — call in onPageChanged
  • TabBar — call from a TabController listener
  • Modals / bottom sheets you want treated as screens
Cxorbi.instance.screen('/checkout');

If your app starts with home: and no named initial route, call screen() when the first view appears; otherwise replay/error/performance capture will keep waiting for a real screen name.

Naming guidance: keep names short and template-based, separate words with /, - or _, and encode state in the name when it changes the layout — e.g. /cart-empty vs /cart.

In-app surveys (Ask) #

The SDK can fetch, render and submit server-driven surveys natively. It is inert by default — turn it on with surveysEnabled: true, then wrap your app once so the SDK can present over your UI without owning the Navigator:

await Cxorbi.init(CxorbiConfig(
  apiKey: 'YOUR_PUBLIC_SOURCE_TOKEN',
  surveysEnabled: true, // opt-in; default false
));

MaterialApp(
  navigatorObservers: [
    CxorbiNavigatorObserver(checkSurveyOnNavigation: true),
  ],
  builder: (context, child) => CxorbiSurveyHost(child: child!),
  // ...
);

After optIn(), eligible surveys auto-display on launch, app-foreground, navigator screen changes when checkSurveyOnNavigation is true, and on Cxorbi.instance.trackEvent(...). You can also drive them manually:

await Cxorbi.instance.showSurvey();          // fetch + present if eligible
final s = await Cxorbi.instance.fetchSurvey(); // fetch only, no UI
Cxorbi.instance.closeSurvey();               // dismiss the active survey

Cxorbi.instance.setSurveyCallbacks(CxorbiSurveyCallbacks(
  onSurveyDisplayed: (s) {},
  onSurveyCompleted: (s, answers) {},
));

All 11 question types, skip logic, dashboard theming and a thank-you card are supported. For regulated apps: consent gates both fetch and submit, the survey overlay never enters session replay, free-text answers are PII-scrubbed before egress (scrubFreeTextPii, default on), and allowedApiHosts can pin egress to approved hosts (fail-closed).

Privacy & masking #

Replays are wireframes: layout boxes, text, colors — never pixels or screenshots. On top of that:

  • Input fields are masked by default. Only explicitly unmask fields after confirming your privacy policy allows it.
  • All other text is masked by default (MaskingMode.text), with the mode controlled from the dashboard (Settings → Recording Privacy). An explicit maskingMode in CxorbiConfig overrides the dashboard.
  • Images and custom render output are placeholders by default. Enable captureImageAssets / captureRenderBoundaryAssets only for reviewed, non-sensitive surfaces.
  • Modes: none (capture text), digits (mask digits only), text / full (mask all text, length-preserving *).

To mask a specific widget regardless of the global mode, wrap it in CxorbiMask:

CxorbiMask(
  child: Text(user.cardNumber),
)

Everything inside a CxorbiMask subtree is masked by default, even when the global mode is none. For scoped overrides:

CxorbiMask(
  config: const CxorbiMaskingConfig(maskTexts: false),
  child: const Text('Public label'),
)

Identify users #

Cxorbi.instance.identify('user-123', {
  'plan': 'premium',
  'country': 'IN',
});

Cxorbi.instance.addUserProperties(properties: {'campaign': 'summer'});

// On logout — clears identity + user/event properties and rotates the session
// so the next user is never mixed into the previous one's replay/heatmaps:
Cxorbi.instance.reset();

Custom events #

Cxorbi.instance.track('feature_used', {'feature': 'dark_mode'});

// Attach properties to every subsequent event:
Cxorbi.instance.addEventProperties(properties: {'app_version': '2.1.4'});

Events feed journeys and funnels alongside automatic screen views.

Transactions #

Cxorbi.instance.trackTransaction(
  orderId: 'ord_001',
  revenue: 4999,        // minor units
  currency: 'USD',
  items: [
    {'sku': 'SKU-1', 'qty': 1},
  ],
);

Errors #

Uncaught Flutter framework errors and 4xx/5xx Dart HttpClient API errors are captured automatically after the first screenview. Query strings are stripped from API error URLs. Mask path segments with path templates:

Cxorbi.instance.setURLMaskingPatterns(patterns: [
  'https://api.example.com/users/:user_id/address/:address',
]);

Report handled errors yourself:

try {
  await api.submit();
} catch (e, st) {
  Cxorbi.instance.reportError(e, st);
}

For native plugin, WebView or custom-client API failures that do not flow through Dart HttpClient, report the failed request explicitly:

Cxorbi.instance.reportNetworkError(
  method: 'POST',
  url: Uri.parse('https://api.example.com/orders/123'),
  statusCode: 500,
);
await Cxorbi.instance.optIn();  // start capture (required once per launch)
Cxorbi.instance.optOut();       // stop all capture immediately

One-token onboarding #

Prefer to start with a single source token? StartConfig builds a CxorbiConfig from one credential and feeds the same engine:

await Cxorbi.init(
  StartConfig.withDatasourceId(id: 'YOUR_PUBLIC_SOURCE_TOKEN').toCxorbiConfig(),
);
await Cxorbi.instance.optIn();

StartConfig.withDatasourceId() is the recommended one-token onboarding path. The token is source-scoped and ingest-only; it cannot access dashboard or administrative APIs. Existing CxorbiConfig(apiKey: ...) and StartConfig.withApiKey() integrations remain supported.

Replay sampling and event-triggered replay #

The dashboard's Replay collection rate applies only to full replay frames. Screen analytics, funnels, journeys, errors, performance, and enabled heatmap data continue at 100%.

When Event-Triggered Replay is enabled, unsampled sessions send masked, short-lived candidate frames. Candidates are not visible in Session Replay and expire unless your app promotes them:

await Cxorbi.instance.triggerReplayForCurrentSession('payment_failed');
await Cxorbi.instance.triggerReplayForCurrentScreen('checkout_validation_failed');

The session trigger retains the whole candidate session, including frames from before the trigger. The screen trigger retains only the current screen-view instance. Because full-session ETR captures pre-trigger context, it can create replay network traffic for sessions that are later discarded.

Sessions #

  • A session id is created at init(). Mobile replay/error/perf capture starts only after both optIn() and the first screenview.
  • Returning after 30 minutes in background starts a new session by default. Shorter app switches resume the existing session. Configure this explicitly with backgroundSessionTimeoutMs; set it to 0 to disable background-time rotation.
  • Cxorbi.instance.sessionId returns the current id.
  • To join a session minted by your backend analytics, pass CxorbiConfig(sessionId: ...).

Configuration reference #

CxorbiConfig field Default Purpose
apiKey — (required) SDK credential; prefer the public source token from Capture Settings → Data sources
ingestKey null Source-scoped public ingest token when using dual-header compatibility
apiUrl https://api.cxorbi.com/api API base URL
dashboardUrl null Enables metadata.sessionReplayUrl
sessionId auto (fl_…) Externally minted session id
userId null Initial user id (else call identify)
maskingMode dashboard setting Explicit masking override
maskingConfig inherit Fine-grained text/input/image/interaction masking
captureFrames true Wireframe replay frames
captureGestures true Taps / swipes / scrolls
enableInteractionsAutocapture true Alias for gesture/heatmap interaction autocapture; set to false only when intentionally disabling interaction capture
captureErrors true Automatic error capture
capturePerformance true App/screen/network performance samples
captureNetworkErrors true Automatic 4xx/5xx API errors through HttpOverrides
captureNativeNetworkErrors true Native/WebView bridge for API error reports
captureFrameVitals true Aggregated Flutter slow/frozen frame vitals sent through performance telemetry
captureNativeDiagnostics true Android process-exit diagnostics and iOS MetricKit crash/hang diagnostics
captureImageAssets false Opt-in unmasked image asset upload for replay
captureRenderBoundaryAssets false Opt-in CxorbiCaptureBoundary custom-render upload
sessionReplayAutoStart true Start replay automatically after first screenview
urlMaskingPatterns [] API error URL path masking patterns
offlineQueueEnabled true Encrypted bounded disk queue on iOS/Android
offlineQueueMaxEntries 200 Max pending requests before oldest-drop
offlineQueueMaxBytes 30 MB Max pending bytes before oldest-drop
maxReplayAssetBytes 512 KB Max single visual replay asset
logLevel warn Diagnostic verbosity (none/error/warn/info/debug/verbose); none silences all SDK output
debug false Alias for logLevel: LogLevel.debug — verbose [cxorbi] request logs
environment production production/staging/development; stamped on every session
debugMode kDebugMode Flags test/debug traffic; set explicitly for staging/profile builds when needed
captureCadenceMs 300 Base replay tree-walk cadence in milliseconds, bounded to a supported range
backgroundSessionTimeoutMs 1800000 (30 min) Background inactivity before the next foreground starts a new session; 0 disables this rotation
sessionHeartbeatIntervalMs 15000 (15 sec) Lightweight foreground activity marker used for accurate duration when unchanged frames are deduplicated; 0 disables it
gzipRequests false Gzip SDK request bodies at or above gzipMinBytes
gzipMinBytes 1024 Minimum uncompressed body size before gzip is applied
qualityConfig enabled defaults Adaptive quality governor config; disables or tunes jank-based replay throttling

Session-list duration is cumulative foreground-active time, not simply lastTimestamp - firstTimestamp. The lifecycle close marker records an exact short visit (for example, 14 seconds) even when it ends before the first 15-second heartbeat. Heartbeats are crash/force-termination checkpoints and live-update signals; they are not the duration measurement granularity. An OS kill that delivers no lifecycle/crash callback can only be reported through the last durable checkpoint, so its final duration is a lower bound.

How capture works (and what it costs) #

The frame walker runs at the configured captureCadenceMs interval, only after Flutter actually painted a new frame, and only re-emits when screen content changed. Frame trees are bounded, and the adaptive quality governor can lower ordinary capture fidelity under sustained jank while preserving periodic keyframes. Gestures are observed on the global pointer router — no GestureDetector wrapping, no interference with your app's gesture handling.

Troubleshooting #

  • No startup logCxorbi.init() not reached; ensure it runs in main() before runApp().
  • Session never appearsoptIn() was not called, or the device can't reach apiUrl (check for HTTP errors with debug: true).
  • Screens all named unknown — routes are unnamed and no manual screen() calls; add CxorbiNavigatorObserver and route names.
  • Text shows as **** — that's the default privacy mode; change it in Dashboard → Settings → Recording Privacy or via maskingMode.
32
likes
0
points
1.13k
downloads

Publisher

unverified uploader

Weekly Downloads

Cxorbi analytics for Flutter: session replay, heatmaps, screens, gestures, funnels, performance, error capture and in-app surveys for iOS and Android.

Homepage

Topics

#analytics #session-replay #heatmaps #monitoring

License

unknown (license)

Dependencies

crypto, cryptography, ffi, flutter, flutter_secure_storage, http, path_provider, sqflite

More

Packages that depend on cxorbi_flutter

Packages that implement cxorbi_flutter