cxorbi_flutter 0.1.0
cxorbi_flutter: ^0.1.0 copied to clipboard
Cxorbi analytics for Flutter: session replay, heatmaps, screens, gestures, funnels, performance and error capture 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:iosorandroidframework— alwaysflutter
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, 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, neverruntimeTypestrings
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 #
import 'package:cxorbi_flutter/csq.dart';
import 'package:flutter/material.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await CSQ().start(
startConfig: StartConfig.withApiKey(
id: 'YOUR_API_KEY', // Dashboard -> Integrations
options: const AnalyticsOptions(
enableInteractionsAutocapture: true,
),
),
);
// The SDK is opted OUT by default — nothing is captured or sent until
// optIn() is called. Call it right away, or after your consent dialog.
await CSQ().optIn();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
navigatorObservers: [CxorbiNavigatorObserver()], // automatic screen tracking
// ...
);
}
}
Verify your integration #
- Run the app — the console prints
Cxorbi Flutter SDK 0.1.0 starting — platform: ios, framework: flutter, thenCxorbi Flutter SDK consent accepted — session: …. - Navigate to the first named screen. Replay/error capture starts at the first screenview, matching CSQ behavior.
- Tap around.
- Open Session Replay in the dashboard — 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.
Screen tracking #
Automatic (recommended) #
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
TabControllerlistener - Modals / bottom sheets you want treated as screens
Cxorbi.instance.screen('/checkout');
// CSQ-style alias:
await CSQ().trackScreenview(screenName: '/checkout');
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.
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 explicitmaskingModeinCxorbiConfigoverrides the dashboard. - Images and custom render output are placeholders by default. Enable
captureImageAssets/captureRenderBoundaryAssetsonly 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',
});
await CSQ().addUserProperties(properties: {'campaign': 'summer'});
Custom events #
Cxorbi.instance.track('feature_used', {'feature': 'dark_mode'});
await CSQ().addEventProperties(properties: {'app_version': '2.1.4'});
await CSQ().trackEvent(eventName: 'feature_used', properties: {'feature': 'dark_mode'});
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:
CSQ().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:
CSQ().reportNetworkError(
method: 'POST',
url: Uri.parse('https://api.example.com/orders/123'),
statusCode: 500,
);
Consent #
await Cxorbi.instance.optIn(); // start capture (required once per launch)
Cxorbi.instance.optOut(); // stop all capture immediately
Sessions #
- A session id is created at
init(). Mobile replay/error/perf capture starts only after bothoptIn()and the first screenview. - Sessions rotate after 5 minutes in background.
Cxorbi.instance.sessionIdreturns the current id.- To join a session minted by your backend analytics, pass
CxorbiConfig(sessionId: ...).
Configuration reference #
CxorbiConfig field |
Default | Purpose |
|---|---|---|
apiKey |
— (required) | Organization API key from the dashboard |
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 |
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 |
debug |
false |
Verbose logging |
How capture works (and what it costs) #
A frame walker runs at most every 300 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.
Gestures are observed on the global pointer router — no GestureDetector
wrapping, no interference with your app's gesture handling.
Troubleshooting #
- No startup log —
Cxorbi.init()not reached; ensure it runs inmain()beforerunApp(). - Session never appears —
optIn()was not called, or the device can't reachapiUrl(check for HTTP errors withdebug: true). - Screens all named
unknown— routes are unnamed and no manualscreen()calls; addCxorbiNavigatorObserverand route names. - Text shows as
****— that's the default privacy mode; change it in Dashboard → Settings → Recording Privacy or viamaskingMode.