Rovenue class

Rovenue's app-facing Dart API. Use the instance singleton.

RovenuePlatform.instance is read lazily (via _platform) on every call — never cached in a field — so tests can swap RovenuePlatform.instance for a fake at any point and this singleton (constructed once, like any static final) immediately picks it up. The three event streams (changes, logs, funnelClaims) apply the same rule at the stream level: the underlying platform stream is subscribed to lazily, exactly once, on the first Dart-side listener (see _StreamHub), and re-read fresh the next time a listener attaches after every previous one has cancelled.

Properties

changes → Stream<RovenueChangeEvent>
Broadcast stream of onChange events: something the SDK's local cache changed and the app should re-read it. Deliberately NOT given last-value replay (unlike funnelClaims, see below): it is a "go re-fetch current state" signal whose target state is always re-readable on demand (entitlementsAll(), remoteConfigAllJson(), …), so a notification missed before a listener attaches costs nothing — the next explicit read already reflects the latest state.
no setter
funnelClaims → Stream<FunnelClaim>
Broadcast stream of funnel claims resolved asynchronously on the native side (e.g. a deferred deep link or clipboard token).
no setter
hashCode → int
The hash code for this object.
no setterinherited
logs → Stream<RovenueLogRecord>
Broadcast stream of log lines forwarded from the Rust core. Plain broadcast for the same reason as changes: a dropped log line before a listener attaches has no correctness impact — logging is best-effort observability, not state the app depends on.
no setter
runtimeType → Type
A representation of the runtime type of the object.
no setterinherited

Methods

claimFromClipboard() → Future<FunnelClaim?>
Reads a funnel token out of the system clipboard and claims it.
claimFromUrl(String url) → Future<FunnelClaim?>
Claims the funnel token carried by url (a deep link / Universal Link), or resolves null without a network call for any non-funnel URL. Forward every incoming URL here from your app's link handler; see extractFunnelToken for the recognised shapes.
claimFunnelToken(String token) → Future<FunnelClaim>
Claim a known funnel token (from a deep link, QR code, or referral code) and resolve it to a subscriber + funnel-answer payload server-side. On success, refreshes entitlements and also emits on funnelClaims. Throws RovenueException with RovenueErrorKind.funnelTokenNotFound / .funnelTokenExpired / .funnelTokenAlreadyClaimed for the corresponding server rejections.
claimInstall([ClaimInstallParams params = const ClaimInstallParams()]) → Future<FunnelClaim?>
Recover a token via the install-attribution endpoint (Android's Play Install Referrer, or an equivalent server-side match) then claim it. Resolves null when no unattributed install matches a pending funnel token — that's the common case, not an error.
claimViaEmail(String email) → Future<void>
Kick off the email magic-link funnel-claim path. Resolution completes later when the link returns to the app (deep link → this resolves via claimFromUrl/funnelClaims, not via this call's own return value).
configure({required String apiKey, String? baseUrl, RovenueLogLevel logLevel = RovenueLogLevel.warn, String? appVersion, String? environment}) → Future<void>
Initialize the SDK. Must be called before any other method here — everything else assumes a configured native core and crashes (fatalError on iOS, IllegalStateException on Android) otherwise. Calling it again replaces the underlying native instance (the equivalent of calling shutdown first). appVersion is optional; when omitted, the native layer auto-reads the host app's version and getAppVersion reports back whatever was actually resolved.
currentUser() → Future<RovenueUser>
Current identity — the anonymous rovenueId plus appUserId once identify has been called. A local read; does not hit the network.
entitlement(String id) → Future<Entitlement?>
Look up one entitlement by id from the local cache — resolves from disk/memory, not a network round trip. null when the id is unknown or not currently granted. A stale cache triggers a background refresh natively; never call refreshEntitlements from an onChange listener reacting to that refresh's own event, or it loops.
entitlementsAll() → Future<List<Entitlement>>
All currently-granted entitlements from the local cache — a cache read, not a network call. Same background staleness-refresh behaviour and onChange re-entrancy footgun as entitlement.
experiment(String key) → Future<ExperimentAssignment?>
This subscriber's variant assignment for experiment key, or null when not enrolled. Unlike the flag getters, a non-null result also records an exposure (best-effort, deduped) for experiment analysis — call this only where the variant assignment actually drives what the user sees, not speculatively, or exposure counts get inflated.
experimentsAll() → Future<List<ExperimentAssignment>>
Every experiment this subscriber is currently enrolled in. Unlike experiment, this does NOT record exposures — a plain cache read for callers that need the full set (e.g. debug UI) without treating enumeration as "shown to the user".
flushAttributes() → Future<int>
Force an immediate flush of buffered attribute mutations to the server, bypassing the background tick. Returns the number of mutations actually sent. Most apps never need this — the background dispatcher already flushes on its own tick and on foreground.
flushSessionEvents() → Future<int>
Force an immediate POST of buffered session events, bypassing the dispatcher's own tick. Returns the number actually sent. Buffered events already survive a process kill, so this is for callers that want the flush to happen now rather than on the next automatic tick.
getAppAccountToken() → Future<String>
Return the current identity scope's Apple appAccountToken (a UUID), creating and persisting one on first call. Pass this to StoreKit's purchase options so the resulting transaction carries the token Apple echoes back — the anchor the receipt/server-to-server notification path uses to bind the transaction to this subscriber. logOut clears it, so a new anonymous scope gets its own token.
getAppVersion() → Future<String?>
The host app version configure resolved: the explicit appVersion argument when one was given, otherwise whatever the native layer auto-read from the platform package manager/bundle. null before configure has run.
getOfferings() → Future<Offerings>
Fetch the project's current offerings from the backend. A network call from the app's perspective; the native/Rust layer falls back to its own last-cached response on a connectivity failure, but an auth/server rejection still throws RovenueException.
getVersion() → Future<String>
The native SDK's own semantic version, for diagnostics — not this Dart package's pubspec.yaml version and not network-derived.
hasResolvedFunnelClaim() → Future<bool>
Whether this install has already successfully claimed a funnel token. A synchronous-feeling local read of persisted install state — does not hit the network. Use it to gate first-launch attribution orchestration so claimFunnelToken/claimInstall/claimViaEmail/ claimFromClipboard/claimFromUrl only run once per install, even across app restarts.
identify(String appUserId) → Future<void>
Link the anonymous device identity to appUserId. The local write always succeeds and fires an onChange event; the server-side POST /v1/identify is best-effort — a failure (offline/5xx) is retried automatically later, not surfaced as an error here. Throws RovenueException on validation failure (e.g. a blank id), and with RovenueErrorKind.subscriberUnresolvable when the server cannot resolve this install's subscriber (corrupt merge history) — the local link stands, unsynced, until the data is repaired.
installId() → Future<String>
Persisted per-install id (inst_<cuid2>), generated on first access. A local read; does not hit the network.
logOut() → Future<void>
Log out the current user: mints a fresh anonymous rovenueId, drops appUserId, and clears scope-bound local state (buffered session events, attributes, the Apple app-account token) so the next identity starts clean. Client-local only — does not contact the server.
noSuchMethod(Invocation invocation) → dynamic
Invoked when a nonexistent method or property is accessed.
inherited
purchase(StoreProduct product, {String? promotionalOfferId, SubscriptionOption? option}) → Future<PurchaseResult>
Purchases product (optionally via a specific Android option), unpacking productId/productType/basePlanId/offerId for the platform-interface call — callers work with the public StoreProduct / SubscriptionOption models, never raw ids, matching the Swift/Kotlin façades' ergonomics rather than the wire shape.
recordSessionEvent(SessionEventKind kind, String occurredAt, {int? durationMs}) → Future<void>
Append one session-lifecycle event (SessionEventKind.open / .background / .close) to the local buffer for later at-least-once delivery — does not itself hit the network. durationMs is only meaningful for .close (foreground session length); leave it null for .open/.background.
refreshEntitlements() → Future<void>
Force a network refresh of entitlements, bypassing the staleness window entitlement/entitlementsAll use. Never call this from inside an onChange listener triggered by entitlements changing — the refresh re-emits that same event, re-invoking the listener, forever.
refreshRemoteConfig() → Future<void>
Force an immediate Remote Config fetch from the server, bypassing the staleness window the getters below use. Never call this from inside a remote-config-changed onChange listener it triggers, or it loops.
refreshVirtualCurrencies() → Future<void>
Force a network refresh of virtual-currency balances, bypassing the staleness window virtualCurrencies/virtualCurrency use. Never call this from inside the balance-changed listener it fires — that re-triggers the listener, forever.
remoteConfigAllJson() → Future<String>
The whole Remote Config payload ({ flags, experiments }) as a JSON string, for callers backing their own reactive/synchronous read layer instead of calling the typed getters one key at a time.
remoteConfigBool(String key, bool fallback) → Future<bool>
Boolean feature-flag value for key from the local cache, or fallback when absent/mistyped. Cache read, never blocks on network; a stale cache triggers a background refresh, so never call refreshRemoteConfig from a remote-config-changed listener this can trigger.
remoteConfigDouble(String key, double fallback) → Future<double>
Floating-point feature-flag value for key from the local cache, or fallback when absent/mistyped. Same cache-read and background staleness-refresh behaviour as remoteConfigBool.
remoteConfigInt(String key, int fallback) → Future<int>
Integer feature-flag value for key from the local cache, or fallback when absent/mistyped. Same cache-read and background staleness-refresh behaviour as remoteConfigBool.
remoteConfigJson(String key) → Future<String?>
Raw JSON string for any present flag (object/array/primitive), or null when key is absent. Use this for structured flag values the typed getters above can't represent; parse it yourself.
remoteConfigKeys() → Future<List<String>>
All feature-flag keys currently present in the local remote-config cache. Cache read; triggers the same background staleness refresh as the typed getters above.
remoteConfigString(String key, String fallback) → Future<String>
String feature-flag value for key from the local cache, or fallback when absent/mistyped. Same cache-read and background staleness-refresh behaviour as remoteConfigBool.
restorePurchases() → Future<PurchaseResult>
Restore the user's previously-purchased entitlements from the store (StoreKit's transaction history / Google Play's owned-purchases query), re-submitting any found receipts so the server can (re-)grant entitlements for this subscriber. Use for the "Restore Purchases" button required by App Store review — entitlements are already synced automatically via configure/purchase receipt posting, so this isn't needed on every launch.
setAttributes(Map<String, String?> attributes) → Future<void>
Queue a batch of subscriber-attribute mutations. Written to the local buffer immediately; the server sync happens in the background (periodic tick, on foreground, or via flushAttributes). A null value deletes the key. Reserved keys (prefixed with $, e.g. $email) map to first-class fields server-side — setEmail/ setDisplayName/setPhoneNumber/setPushToken are thin wrappers over this for the common ones.
setDisplayName(String? name) → Future<void>
Subscriber's display name → the $displayName reserved attribute. Same buffered-write, background-flush contract as setAttributes; null clears it.
setEmail(String? email) → Future<void>
Subscriber's email → the $email reserved attribute. Same buffered-write, background-flush contract as setAttributes; null clears it.
setFallbackPlacements(String json) → Future<int>
Load a bundled fallback-placements file (once, replacing any previously-loaded set) so placements keep resolving offline when both network and disk cache miss. Returns the count of entries actually loaded; a malformed file throws RovenueException (RovenueErrorKind.invalidArgument).
setForeground(bool foreground) → Future<void>
Tell the native core whether the host app is foregrounded, gating background polling and triggering an immediate drain of the queued event queue on the foreground transition. Wire this to AppLifecycleState changes (WidgetsBindingObserver).
setPhoneNumber(String? phone) → Future<void>
Subscriber's phone number → the $phoneNumber reserved attribute. Same buffered-write, background-flush contract as setAttributes; null clears it.
setPushToken(String? token) → Future<void>
Device push token → the platform's reserved attribute ($apnsTokens on iOS, $fcmTokens on Android — the native layer picks the right key). Same buffered-write, background-flush contract as setAttributes; null clears it.
shutdown() → Future<void>
Stop the native core's background polling (entitlements/remote config). Most apps never need this directly — configure already tears down and replaces the previous instance when called again.
toString() → String
A string representation of this object.
inherited
track(String envelopeJson) → Future<void>
Emit a generic event (fire-and-forget, retried a few times internally). envelopeJson is the camelCase wire envelope; when it omits subscriberId, the native layer fills it from the current identity scope.
virtualCurrencies() → Future<Map<String, int>>
All virtual-currency balances, keyed by currency code, from the local cache — a cache read, not a network call. Stale balances trigger a background refresh natively; never call refreshVirtualCurrencies from an onChange listener reacting to that refresh's own event.
virtualCurrency(String code) → Future<int>
One virtual-currency balance by code from the local cache (0 when the code is unknown — never an error). Same cache-read and re-entrancy footgun as virtualCurrencies.

Operators

operator ==(Object other) → bool
The equality operator.
inherited

Static Properties

instance → Rovenue
final