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

retracted

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 0.2.0 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 (matches Contentsquare best practice): 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 CS-style patterns:

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

Contentsquare-compatible API #

Migrating from Contentsquare, or prefer that shape? A drop-in CSQ() facade mirrors the Contentsquare Flutter surface and forwards to the same engine:

await CSQ().start(
  startConfig: StartConfig.withDatasourceId(id: 'YOUR_PUBLIC_SOURCE_TOKEN'),
);
await CSQ().optIn();
await CSQ().trackScreenview(screenName: '/checkout');
await CSQ().trackEvent(eventName: 'feature_used', properties: {'feature': 'dark_mode'});

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.
  • During replay testing, returning after 2 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 CS-compatible 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
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 5 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, clamped 50..10000
backgroundSessionTimeoutMs 120000 (2 min) Temporary testing default: 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 (default 300 ms, clamped 50..10000 ms), only after Flutter actually painted a new frame, and only re-emits when screen content changed (an idle screen sends a keyframe every 10 s). Trees are capped at 800 nodes / depth 60, and the adaptive quality governor can lower ordinary capture fidelity under sustained jank while keeping the 10-second keyframe floor. 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