Skip to content

Flutter

Identify your users and track product events so Whisperr can decide and deliver churn-prevention interventions. Two calls do the work: identify() and track().

dependencies:
whisperr: ^0.2.3

Published on pub.dev as whisperr.

Call once at startup (e.g. in main). Get an app ingestion key from the Whisperr dashboard → Developer → API Keys.

import 'package:whisperr/whisperr.dart';
await Whisperr.initialize(apiKey: 'wrk_xxx');

baseUrl defaults to https://api.whisperr.net; pass it only to target a self-hosted or local backend.

Set who the current user is. Idempotent and safe to call on every login. Traits are merged server-side; channels are how Whisperr can reach the user — and whether it’s allowed to.

// Common case — email/phone/pushToken expand into opted-in channels:
await Whisperr.instance.identify(
'user_123',
email: 'ada@example.com',
phone: '+15551234567',
pushToken: fcmToken, // expands to an opted-in push channel
traits: {'name': 'Ada', 'plan': 'pro'},
);
// Full control — consent and verification:
await Whisperr.instance.identify(
'user_123',
channels: [
WhisperrChannel.email('ada@example.com', verified: true),
WhisperrChannel.sms('+15551234567', optedIn: false), // opted out of SMS
],
);

Whisperr decides which channel to actually use based on engagement — express an explicit user choice via optedIn: false on the channels they don’t want.

The SDK never bundles a push library — hand it the token your messaging setup (e.g. firebase_messaging) produces and Whisperr keeps the push channel current. setPushToken is safe to call on every launch and from onTokenRefresh: repeated tokens are a no-op, a rotated token opts the previous one out, and a token set before login buffers and attaches to the next identify().

import 'package:firebase_messaging/firebase_messaging.dart';
final messaging = FirebaseMessaging.instance;
// Current token:
final token = await messaging.getToken();
if (token != null) await Whisperr.instance.setPushToken(token);
// Rotations, forwarded automatically:
final sub = Whisperr.instance.attachPushTokenStream(messaging.onTokenRefresh);

After reset() (logout), call setPushToken again once the next user logs in.

Record product events. Buffered and sent in batches; the timestamp is captured at call time, so events recorded offline keep their real time.

Whisperr.instance.track('checkout_completed', properties: {'amount': 42, 'currency': 'USD'});

Event names must be snake_case — see Event design. Only events that map to the events you configured during onboarding drive interventions; others are accepted but inert.

await Whisperr.instance.reset(); // flushes, then clears the current user
  • Durable queueidentify and track append to an ordered queue and deliver in order; track calls coalesce into /v1/events/batch.
  • Batching — flushes on an interval, when the buffer hits flushAt, on app pause/detach, or when you call flush().
  • Offline — the queue is persisted (via shared_preferences) and survives app restarts. Transient failures retry with exponential backoff, auth errors pause delivery and keep the queue, permanent client errors drop the offending item — the standard delivery contract.
await Whisperr.initialize(
apiKey: 'wrk_xxx',
options: const WhisperrOptions(
flushInterval: Duration(seconds: 15),
flushAt: 20,
maxBatchSize: 500, // backend hard cap
maxQueueSize: 1000, // drops oldest beyond this
enablePersistence: true,
debug: false,
),
);
await Whisperr.instance.flush(); // force delivery (e.g. before a critical await)

The ingestion key is embedded in your app, like a Segment write key or Amplitude API key. It can only ingest events for your app; treat it as publishable, not secret.