nexus_flutter 1.0.2
nexus_flutter: ^1.0.2 copied to clipboard
Nexus — one Flutter SDK for all Inverge Nexus services (realtime, sessions, events, errors, logs, feature flags, remote config, links, session replay).
Nexus Flutter SDK — Full Reference #
nexus_flutter is the umbrella client for the Inverge Nexus platform. One
initialization gives you sessions, product analytics, structured logging,
error/crash monitoring, feature flags, remote config, deep‑link attribution,
realtime messaging, session replay, and in‑product surveys — all correlated to
a single user journey.
- Package:
nexus_flutter· version1.0.0 - Platforms: Android, iOS, Web, macOS, Windows, Linux
- Dart SDK:
^3.13.0
Table of contents #
- Installation
- Initialization
- Configuration reference
- Accessing the SDK
- Identity
- Sessions
- Events (analytics)
- Logs
- Errors & crashes
- Feature flags
- Remote Config
- Deep links & attribution
- Realtime
- Session replay
- Surveys
- Lifecycle, flushing & disposal
1. Installation #
Add the dependency (from a path, git, or pub once published):
dependencies:
nexus_flutter: ^1.0.1
flutter pub get
No native setup is required for the core products. Session replay and native crash capture work out of the box through the bundled platform channel.
2. Initialization #
Call Nexus.init once, before runApp, then wrap your app in NexusScope
(required for session replay and for context.nexus).
import 'package:flutter/material.dart';
import 'package:nexus_flutter/nexus.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Nexus.init(const NexusConfig(
apiKey: 'nxs_live_xxx',
baseUrl: 'https://services.inverge.net',
// opt‑in products:
remoteConfigEnabled: true,
surveysEnabled: true,
replayEnabled: false,
));
runApp(const NexusScope(child: MyApp()));
}
Nexus.init(NexusConfig) → Future<Nexus>. Re‑calling init disposes the
previous instance. Nexus.isInitialized reports whether it has run.
3. Configuration reference #
Every field of NexusConfig (all optional except apiKey):
| Field | Type | Default | Purpose |
|---|---|---|---|
apiKey |
String |
— | Tenant API key (nxs_…). Sent as x-api-key and in the socket handshake. |
baseUrl |
String |
https://services.inverge.net |
API origin. Partner endpoints live under /partner. |
realtimeUrl |
String? |
baseUrl |
Socket.IO origin, if different. |
autoTrackSessions |
bool |
true |
Start & keep a journey session automatically. |
autoCaptureErrors |
bool |
true |
Install Flutter/Dart error handlers. |
autoConnectRealtime |
bool |
false |
Open the socket at startup (connection‑minutes are billed). |
manageRealtimeWithLifecycle |
bool |
true |
Disconnect on background / reconnect (rejoining rooms) on foreground for accurate metering. |
flushOnBackground |
bool |
true |
Flush queued telemetry when backgrounded. |
flushInterval |
Duration |
10s |
Batch flush cadence for events/logs. |
maxBatch |
int |
50 |
Max items per flush. |
logging |
bool |
false |
Shorthand for debug log level. |
logLevel |
NexusLogLevel? |
warn |
SDK diagnostic verbosity. |
onLog |
NexusLogSink? |
— | Receive every SDK log record. |
replayEnabled |
bool |
false |
Record session replay (needs NexusScope). |
replayInterval |
Duration |
1s |
Replay frame cadence. |
replayPixelRatio |
double |
1.0 |
Replay capture resolution multiplier. |
replayMaskTextFields |
bool |
true |
Redact all text inputs from replay. |
replayCaptureConsole |
bool |
true |
Tee debugPrint into the replay console tab. |
replayCaptureNetwork |
bool |
true |
Auto‑capture HTTP for the replay network tab. |
surveysEnabled |
bool |
false |
Fetch eligible surveys at startup/foreground. |
surveyAutoShow |
bool |
true |
Auto‑present popover/banner + event‑triggered surveys. |
remoteConfigEnabled |
bool |
false |
Fetch Remote Config at startup/foreground. |
remoteConfigRealtime |
bool |
false |
Re‑fetch config live on publish (needs a realtime connection). |
remoteConfigDefaults |
Map<String,Object?> |
{} |
In‑app default config values. |
appVersion |
String? |
— | App version reported with telemetry. |
defaultProperties |
Map<String,Object?> |
{} |
Merged into every event/person context. |
4. Accessing the SDK #
Two equivalent ways:
// 1. Global singleton (anywhere):
Nexus.instance.events.track('opened_cart');
// 2. From a BuildContext under NexusScope:
context.nexus.events.track('opened_cart');
Services exposed on the instance: sessions, events, logs, errors,
flags, remoteConfig, links, realtime, replay, surveys.
5. Identity #
// Name the current end‑user (call after login). Attributes all telemetry to it.
await Nexus.instance.identify(
'user_123',
email: 'a@b.com',
name: 'Ada',
traits: {'plan': 'pro', 'governorate': 'Erbil'},
);
// On logout — forget the user and start a fresh session:
Nexus.instance.reset();
Nexus.instance.distinctId; // String? — current user id (null if anonymous)
Nexus.instance.sessionKey; // String — current session key
identify is shorthand for sessions.identify and also refreshes the session.
6. Sessions #
nexus.sessions — the journey spine every other product correlates into.
// Identify (same as Nexus.identify):
await nexus.sessions.identify('user_123', email: 'a@b.com', name: 'Ada',
traits: {'plan': 'pro'});
// Start/refresh the active session; returns the server session id:
final String? sid = await nexus.sessions.track();
// Forget the user + rotate the session:
nexus.sessions.reset();
With autoTrackSessions: true (default) a session is started automatically at
init and refreshed on foreground — you rarely call track() yourself.
7. Events (analytics) #
nexus.events — buffered, batched, durably queued, and session‑correlated.
nexus.events.track('order_placed', properties: {'total': 42.0, 'currency': 'USD'});
nexus.events.track('screen_view', properties: {'name': 'checkout'});
// Force an immediate flush of the batch:
await nexus.events.flush();
// Fire a callback for every tracked event (used internally for survey triggers):
nexus.events.onTracked = (name) => print('tracked $name');
defaultProperties from config are merged into every event.
8. Logs #
nexus.logs — structured, batched, session‑correlated logging.
nexus.logs.trace('entering checkout');
nexus.logs.debug('cart snapshot', context: {'items': 3});
nexus.logs.info('payment started', source: 'checkout');
nexus.logs.warn('retatrying charge', context: {'attempt': 2});
nexus.logs.error('charge failed', source: 'stripe', context: {'code': 'card_declined'});
await nexus.logs.flush();
Each level accepts {String? source, Map<String,Object?>? context}.
9. Errors & crashes #
nexus.errors — with autoCaptureErrors: true (default), every uncaught
error is reported automatically: Flutter framework errors, uncaught async Dart
errors, and native crashes (forwarded on the next launch). You can also report
handled errors manually:
try {
await risky();
} catch (e, st) {
await nexus.errors.capture(e, st, {
'handled': true, // false marks an uncaught crash
'level': 'error', // trace|debug|info|warn|error|fatal
'feature': 'checkout', // any extra context
});
}
capture(Object error, [StackTrace? stack, Map<String,Object?>? extra]). The
handled and level keys in extra are special; the rest is attached as
context. Wrap your app body in runZonedGuarded for the broadest async capture
(the SDK also hooks FlutterError.onError and PlatformDispatcher.onError).
10. Feature flags #
nexus.flags — evaluate once, then read synchronously.
await nexus.flags.load(properties: {'plan': 'pro'});
nexus.flags.isEnabled('new_checkout'); // bool
nexus.flags.variant('paywall'); // String? (multivariate)
nexus.flags.payload('paywall'); // Object? (attached JSON payload)
nexus.flags.all; // Map<String,Object?> of every flag
load({Map<String,Object?>? properties}) evaluates all flags for the current
distinctId + person properties and caches them. Call it after identify and
whenever targeting properties change.
For arbitrary typed configuration (not just on/off), prefer Remote Config.
11. Remote Config #
nexus.remoteConfig — Firebase‑style typed parameters with server‑evaluated
conditions (platform, version, country, percentile rollout, custom
attributes…). Enable with remoteConfigEnabled: true.
Defaults & fetch #
// In‑app fallbacks (used until/unless the server has a value):
nexus.remoteConfig.setDefaults({'welcome': 'Hi', 'max_items': 10, 'phone_number': '+964...'});
// Fetch + activate the published template for this device:
await nexus.remoteConfig.fetch();
Typed getters #
nexus.remoteConfig.getString('welcome'); // String ('' fallback)
nexus.remoteConfig.getBool('feature_enabled'); // bool (false fallback)
nexus.remoteConfig.getInt('max_items'); // int (0 fallback)
nexus.remoteConfig.getDouble('price'); // double (0 fallback)
nexus.remoteConfig.getJson('theme'); // Object? (Map/List)
nexus.remoteConfig.getValue('welcome'); // Object? (raw)
nexus.remoteConfig.getAll(); // Map<String,Object?>
nexus.remoteConfig.sourceOf('phone_number'); // String? condition name, or null (default)
nexus.remoteConfig.version; // int active template version
nexus.remoteConfig.lastFetchTime; // DateTime?
Each typed getter takes an optional fallback: getString('k', 'fallback').
Custom targeting attributes (e.g. governorate) #
Send arbitrary key/values the server matches with Custom attribute conditions. Persistent attributes are sent on every fetch; per‑fetch attributes apply to one call.
// Persistent (recommended for things like governorate / plan / segment):
nexus.remoteConfig.setAttribute('governorate', 'Erbil');
nexus.remoteConfig.setAttributes({'plan': 'pro', 'segment': 'beta'});
await nexus.remoteConfig.fetch();
// One‑off for a single fetch:
await nexus.remoteConfig.fetch(attributes: {'governorate': 'Duhok'});
nexus.remoteConfig.attributes; // Map<String,Object?> (unmodifiable)
nexus.remoteConfig.setAttribute('governorate', null); // remove one
nexus.remoteConfig.clearAttributes();
Merge precedence (later wins): defaultProperties → identity traits →
setAttributes → per‑fetch attributes. Device fields (platform, appVersion,
country/language) are filled in automatically.
Values must be primitives (string/number/bool) and match the condition value exactly (case‑sensitive). If you compare against a governorate name,
React to changes (realtime) #
// Rebuild UI whenever config changes:
final sub = nexus.remoteConfig.onChange.listen((_) => setState(() {}));
// Push updates: with a realtime connection + remoteConfigRealtime: true, the SDK
// re‑fetches automatically when you publish a new template. Otherwise call
// subscribeRealtime once you have a connection:
await nexus.remoteConfig.subscribeRealtime(nexus.realtime);
await nexus.remoteConfig.unsubscribeRealtime();
Example — dynamic support number by region #
await Nexus.init(const NexusConfig(
apiKey: 'nxs_...',
remoteConfigEnabled: true,
remoteConfigDefaults: {'phone_number': '+9640000000000'},
));
nexus.remoteConfig.setAttribute('governorate', 'Duhok');
await nexus.remoteConfig.fetch();
final phone = nexus.remoteConfig.getString('phone_number'); // region‑specific
12. Deep links & attribution #
nexus.links — OneLink‑style deferred deep linking + install/open attribution.
// On first open, report the install/open and receive deferred deep‑link data:
final data = await nexus.links.attribute(
type: 'install', // install | open | reengagement | ...
clickId: 'abc', // from the link (deterministic match)
name: 'summer_sale',
platform: 'ios',
properties: {'campaign': 'promo'},
);
// Convenience: parse an incoming deep link URI and attribute an open:
final deepLink = await nexus.links.handleDeepLink(incomingUri); // reads link_click_id
Both return the matched link's deep‑link data (Map) or null if unattributed.
13. Realtime #
nexus.realtime — Socket.IO messaging authenticated with the API key + journey
identity. Connection‑minutes are billed, so it is opt‑in.
nexus.realtime.connect(); // idempotent; rejoins rooms on reconnect
nexus.realtime.isConnected; // bool
await nexus.realtime.join('orders:42'); // {ok, room, related}
await nexus.realtime.leave('orders:42'); // {ok}
// Emit one or more events to a room:
await nexus.realtime.emit('orders:42', 'status', {'state': 'shipped'});
// Listen for server events:
nexus.realtime.on('status', (data) => print(data));
nexus.realtime.off('status');
nexus.realtime.disconnect();
With autoConnectRealtime: true the socket opens at startup; with
manageRealtimeWithLifecycle: true (default) it disconnects on background and
reconnects (rejoining rooms) on foreground.
14. Session replay #
nexus.replay — rrweb‑compatible screenshot frames + pointer events, all
platforms. Enable with replayEnabled: true and wrap the app in NexusScope.
runApp(const NexusScope(child: MyApp())); // installs the capture RepaintBoundary
Redact sensitive widgets:
NexusMask(child: CreditCardWidget()); // excluded from frames
Navigation, console, and network capture are automatic (see config). Manual hooks:
nexus.replay.trackScreen('checkout'); // populate the Pages tab manually
nexus.replay.observeRouter(router, () => currentPath); // GoRouter / nested routes
nexus.replay.recordNetwork( // feed from your HTTP interceptor
url: 'https://api…', method: 'GET', status: 200, durationMs: 120, size: 2048,
);
Or attach the navigator observer directly:
MaterialApp(navigatorObservers: [NexusNavigatorObserver()]);
15. Surveys #
nexus.surveys — PostHog/AppsFlyer‑style in‑product surveys. Enable with
surveysEnabled: true and mount the overlay in your MaterialApp.builder.
MaterialApp(
builder: (context, child) =>
NexusSurveyOverlay(child: child ?? const SizedBox.shrink()),
home: const HomePage(),
);
API #
await nexus.surveys.fetch(); // List<NexusSurvey> eligible for the user
nexus.surveys.active; // List<NexusSurvey> (cached)
nexus.surveys.byId('survey_1'); // NexusSurvey?
nexus.surveys.show(survey); // present a specific survey now
nexus.surveys.close(); // remove the current survey
// Submit answers (partial, complete, or dismissed):
await nexus.surveys.respond(
survey,
{'q1': 9, 'q2': 'Great!'},
completed: true,
dismissed: false,
);
// The survey the overlay should render right now (ValueNotifier):
nexus.surveys.current; // ValueNotifier<NexusSurvey?>
Custom UI #
Replace the default rendering with your own widgets via builders:
NexusSurveyOverlay(
child: child,
// Fully custom survey chrome, driven by a controller:
surveyBuilder: (context, controller) => MyCustomSurvey(controller),
// Or keep the default chrome but customize each question:
questionBuilder: (context, question, controller) => MyQuestion(question, controller),
);
NexusSurveyController (a ChangeNotifier) exposes the survey, current
answers, and submit/close. NexusSurvey/NexusSurveyQuestion/
NexusSurveyChoice/NexusSurveyTrigger are the data models (question type,
choices, scaleMin/Max, display, etc.).
16. Lifecycle, flushing & disposal #
await Nexus.instance.flush(); // flush all batched telemetry now
Nexus.instance.dispose(); // tear down (also disposes on re‑init)
The SDK installs lifecycle hooks automatically: on background it flushes telemetry, pauses replay, and (if configured) disconnects realtime for accurate metering; on foreground it refreshes the session, drains the durable outbox, re‑fetches surveys/remote‑config, and reconnects realtime.