Browse docsOverview
Flutter SDK

Flutter SDK

Reflect SDK for Flutter — a thin Dart + native bridge over the shared ReflectCore engine. Mobile attribution, event tracking, deep linking, and SKAN for iOS and Android.

Upgrade to v1.7.3 — every release through v1.7.1 shipped no privacy engine

Releases up to and including v1.7.1 pinned Android core com.github.bablu147:reflect-android:1.0.0, which contained no privacy engine at all — no transport gate, no fail-closed posture, no deletion journal, and no 90-day click-context retention. v1.7.3 pins reflect-android:1.1.2and resolves ReflectCore ~> 1.1 (currently 1.1.4) on iOS — the 1.1.x privacy line (1.1.0 is the floor). Upgrading is strongly recommended.

The plugin itself remains a thin wrapper over the shared native core — the same Kotlin (reflect-android) and Swift (reflect-ios) core the React Native and Unity SDKs use. All logic (durable on-disk queue, HMAC-signed ingest, batching, response-driven retry, device signals, deferred deep links, attribution, SKAN, ATT) lives in the core; the Dart layer only forwards each call over a method channel. 1.6.0 and earlier were a standalone re-implementation that posted unsigned events. Your install_uuid is preserved across the upgrade.

Installation

The plugin is not published on pub.dev — install it from a git tag. Add the dependency to your pubspec.yaml, pinned to a release tag:

dependencies:
  reflect_sdk:
    git:
      url: https://github.com/bablu147/reflect-sdk-flutter.git
      ref: v1.7.3

Then run flutter pub get. Because the plugin pulls its native core as a versioned dependency, a consumer app needs two one-time additions so the published cores resolve.

Android — you must add the JitPack repository to your own app's Gradle build. A Gradle plugin cannot inject a repository into the consuming build, so there is no way for the SDK to do this for you; without it the build fails withCould not find com.github.bablu147:reflect-android:1.1.2. Which file it goes in depends on the Flutter template your app was generated from — use whichever file already lists google() and mavenCentral().

Modern template (Kotlin DSL android/build.gradle.kts, with a settings.gradle.kts that only carries pluginManagement) — the root allprojects block:

// android/build.gradle.kts
allprojects {
  repositories {
    google()
    mavenCentral()
    maven { url = uri("https://jitpack.io") }   // com.github.bablu147:reflect-android
  }
}

Older template — if your android/settings.gradle already has a dependencyResolutionManagement block, add it there instead:

// android/settings.gradle
dependencyResolutionManagement {
  repositories {
    google()
    mavenCentral()
    maven { url 'https://jitpack.io' }   // com.github.bablu147:reflect-android
  }
}

The AAR brings its transitive dependencies (Play Install Referrer, App Set ID, ads-identifier) and R8 keep-rules automatically.

iOSReflectCore is not published on CocoaPods trunk, and CocoaPods will not follow the plugin's podspec dependency to a git source. You must declare the pod explicitly in your ios/Podfile, pinned to the tag below, before running cd ios && pod install:

pod 'ReflectCore', :git => 'https://github.com/bablu147/reflect-ios.git', :tag => '1.1.4'

Requirements

  • Dart >=3.0.0, Flutter >=3.10.0
  • Android: minSdkVersion 21
  • iOS: 13+
  • Pulls the shared native core — com.github.bablu147:reflect-android:1.1.2 from JitPack on Android, the ReflectCore pod (~> 1.1, currently 1.1.4) on iOS — and no other third-party dependencies

Quick start

import 'package:reflect_sdk/reflect_sdk.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await Reflect.initialize(ReflectConfig(
    appKey:        "app_live_...",
    companyKey:    "co_live_...",
    baseUrl:       "https://api.reflect.cloud",
    // signingSecret: "sec_...",  // optional — HMAC-signs ingest + surfaces attribution
  ));

  runApp(MyApp());
}

With a signingSecret set, every event batch is HMAC-signed and posted to the authenticated /event endpoint; without one the SDK falls back to the unsigned /event/batch path. baseUrl already defaults to https://api.reflect.cloud. Events from this release carry sdk_version flutter-1.7.3 on the wire — use it to confirm in the dashboard that your build picked up the upgrade.

API reference

MethodDescription
Reflect.initialize(config)Initialize the SDK (call once at app start)
Reflect.trackEvent(name, properties?)Track a named event with optional properties
Reflect.trackRevenue(params)Track a revenue event
Reflect.trackPurchase(params)Track a purchase event with optional receipt
Reflect.trackSubscription(params)Track a subscription event
Reflect.verifyPurchase(params)Verify a receipt server-side and return a typed result
Reflect.verifyAndTrackPurchase(params)Verify a receipt AND track the purchase
Reflect.trackAdRevenue(params)Track mediated ad revenue (fires ad_impression)
Reflect.setUserId(userId)Set user ID for cross-device attribution
Reflect.clearUserId()Clear user ID (e.g. on logout)
Reflect.setEmail(email)Attach the user's RAW email to future events (server hashes it; consent-gated) — enables email attribution
Reflect.clearEmail()Clear the stored email (e.g. on logout)
Reflect.setUserProperties(props)Set properties attached to all future events
Reflect.setGlobalProperty(key, val)Set a property merged into every event
Reflect.unsetGlobalProperty(key)Remove a global property
Reflect.clearGlobalProperties()Remove all global properties
Reflect.setPartnerParameter(key, val)Set a global partner parameter forwarded to integrations
Reflect.setPartnerSharing(map)Per-partner third-party-sharing overrides
Reflect.setThirdPartySharing(enabled)Opt in/out of partner data sharing
Reflect.setConsent(state) / getConsent()Set/read data-collection consent (attached as consent_state)
Reflect.setAdvertisingConsent(granted)Grant/revoke IDFA/GAID consent
Reflect.setExternalDeviceId(id)Attach a customer-owned device identifier
Reflect.requestIosTracking()Request iOS ATT permission (returns status)
Reflect.getInstallUuid()Get the persistent install UUID
Reflect.getAttribution()Get current attribution data
Reflect.getAttributionWithTimeout(ms)Force a fresh attribution check, resolving early or after the timeout
Reflect.onAttributionChangedStream firing when the server resolves/changes attribution
Reflect.updateConversionValue(...)Update SKAN conversion value (iOS only)
Reflect.getInitialDeepLink()Get the deep link that opened the app
Reflect.getLastDeepLink()The last deep link handled (cold/warm/deferred)
Reflect.onDeepLinkStream of deep link events while running
Reflect.resolveDeepLink(url)Unshorten/resolve a branded tracking URL via the server
Reflect.handleDeepLink(url)Feed the SDK a link the app captured itself
Reflect.registerPushToken(token, provider)Register a push token (fires _push_token)
Reflect.setPushToken(token)Sticky push token stamped on every event
Reflect.setIntegrityToken(token)Supply a Play Integrity / App Attest token (anti-fraud)
Reflect.setAudience(tags)Tag the install for audience segmentation
Reflect.deleteUserData()GDPR — delete all user data
Reflect.setEnabled(enabled) / isEnabled()Enable/disable the SDK at runtime
Reflect.setOfflineMode(offline)Pause/resume sending (keeps tracking + queuing on-device)
Reflect.flush()Force-flush buffered events
Reflect.debugSnapshot()PII-safe snapshot of SDK state (drives ReflectDebugOverlay)

Event tracking

// Simple event
await Reflect.trackEvent("level_complete", properties: {
  "level": 5,
  "score": 12340,
});

// Revenue event
await Reflect.trackRevenue(RevenueParams(
  amount: 4.99,
  currency: "USD",
  productId: "gems_500",
  transactionId: "txn_abc123",
));

// Attribute installs/revenue to an email (pass it RAW — the server hashes it;
// only sent while consent is not denied).
await Reflect.setEmail("[email protected]");
await Reflect.clearEmail();   // on logout

The SDK sends the event property bag on the wire as properties; the server maps it to its canonical props field, so props.email reaches attribution. See Privacy & consent for the consent gate and hashing details.

Deep linking

// Get the link that launched the app (incl. server-resolved deferred links)
final initial = await Reflect.getInitialDeepLink();
if (initial != null) {
  navigateTo(initial.path);
}

// Listen for links while running (isDeferred = true for deferred links)
Reflect.onDeepLink.listen((data) {
  navigateTo(data.path);
});

SKAN (iOS)

final result = await Reflect.updateConversionValue(
  42,
  coarseValue: "high",
  lockWindow: false,
);
if (!result.success) print("SKAN error: ${result.error}");

Standard events

Pre-built helpers with typed parameters:

import 'package:reflect_sdk/reflect_sdk.dart';

ReflectStandardEvents.signUpWith("google");
ReflectStandardEvents.levelCompleted(5, score: 12340);
ReflectStandardEvents.addedToCart("gems_500", 4.99, "USD");
ReflectStandardEvents.adShown("admob", "interstitial", revenue: 0.02, currencyCode: "USD");

Global properties

await Reflect.setGlobalProperty("ab_group", "variant_b");
await Reflect.unsetGlobalProperty("ab_group");
await Reflect.clearGlobalProperties();

Purchase & subscription

await Reflect.trackPurchase(PurchaseParams(
  productId: "gems_500",
  price: 4.99,
  currency: "USD",
  receiptData: "<base64-receipt>",
));

await Reflect.trackSubscription(SubscriptionParams(
  productId: "pro_monthly",
  price: 9.99,
  currency: "USD",
  isTrial: true,
));

// Verify a receipt server-side AND track it in one call
final result = await Reflect.verifyAndTrackPurchase(PurchaseParams(
  productId: "gems_500",
  transactionId: "txn_abc123",
  receiptData: "<base64-receipt>",
));

Privacy (GDPR)

final deleted = await Reflect.deleteUserData();
// Clears local state + sends deletion request to server
v1.7.2 — shared native core, signed ingest
Running on the shared ReflectCore, the SDK HMAC-signs ingest, and gains server-side receipt verification (verifyPurchase/verifyAndTrackPurchase), mediated ad revenue (trackAdRevenue), partner parameters & sharing controls, attestation tokens (setIntegrityToken), link resolution (resolveDeepLink/handleDeepLink), offline-mode toggles, and a live debug snapshot — on top of the existing track, consent, deep-link, ATT, and SKAN API. All APIs are async (Future<T>); deep links and attribution use streams.