postback_flutter 1.1.0 copy "postback_flutter: ^1.1.0" to clipboard
postback_flutter: ^1.1.0 copied to clipboard

Flutter Postback attribution SDK.

Postback for Flutter #

Mobile attribution and event tracking for Flutter apps, backed by the native iOS and Android Postback SDKs. The Dart layer is a thin pass-through to the same engines as our standalone iOS and Android SDKs, so behavior matches across platforms.

Requirements #

  • Flutter 3.22 or later
  • Dart 3.3 or later
  • iOS 14.0 or later
  • Android 7.0 (API 24) or later

Install #

Add the package to pubspec.yaml:

dependencies:
  postback_flutter: ^1.1.0

Fetch dependencies:

flutter pub get

The Flutter plugin manages the iOS pod and the Android AAR for you. No extra repository setup needed.

Configure #

Call configure once in main(), before runApp. It returns a future that resolves after local state is restored; install registration runs in the background:

import 'package:flutter/material.dart';
import 'package:postback_flutter/postback_flutter.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await Postback.instance.configure(
    const PostbackConfig(apiKey: 'YOUR_API_KEY'),
  );

  runApp(const MyApp());
}

If you prefer the named-argument form for parity with web SDKs:

await Postback.instance.configure(
  'YOUR_API_KEY',
  endpointBaseUrl: 'https://api.postback.sh',
);

Configuration options #

Option Type Default What it does
apiKey String required Your Postback app key.
apiUrl String https://api.postback.sh Override for staging or self-hosted environments.
endpointBaseUrl String alias for apiUrl Accepted for compatibility.
enableAppleAdsAttribution bool true iOS only. Fetches Apple AdServices at install time.
customerUserId String? null Your internal user ID. Persists across launches and replays if the first send fails.
autoTrackSessions bool true Fires session_start on configure() and on foreground, debounced to one event per 30 minutes.
autoRefreshAttribution bool true Refreshes attribution from the backend on configure() and on foreground.
isDebug bool false Forces debug-level logging on the native side.
logLevel int 2 0 = debug, 1 = info, 2 = warn, 3 = error.

Track events #

import 'package:postback_flutter/postback_flutter.dart';

await Postback.instance.sendEvent(PostbackEventType.login);
await Postback.instance.sendEvent(PostbackEventType.signUp);

await Postback.instance.sendEvent(
  PostbackEventType.purchase,
  params: {
    'revenue': 9.99,
    'currency': 'USD',
  },
);

await Postback.instance.sendEvent(
  PostbackEventType.custom,
  name: 'onboarding_step',
  params: {
    'screen': 'welcome',
    'step': 1,
  },
);

sendEvent resolves to true once the native side has queued the event locally. It resolves to false when a custom event is ignored because its name is missing or invalid. The actual HTTP send happens on the next flush trigger.

Built-in event types #

session_start, login, sign_up, register, purchase, subscribe, start_trial, add_payment_info, add_to_cart, add_to_wishlist, initiate_checkout, view_content, view_item, search, share, tutorial_complete, achieve_level, level_start, level_complete, custom.

Revenue events #

Pass revenue (or price as an alias) plus currency. Currency is trimmed, must contain exactly three ASCII letters, and is normalized to uppercase. An invalid currency is omitted while the event still sends.

await Postback.instance.sendEvent(
  PostbackEventType.subscribe,
  params: {
    'revenue': 4.99,
    'currency': 'EUR',
    'plan': 'monthly',
  },
);

Custom events #

await Postback.instance.sendEvent(
  PostbackEventType.custom,
  name: 'level_skip',
  params: {'level': 12},
);

Custom events require a name containing 1–255 UTF-16 code units after trimming, with no NUL (U+0000) characters. A custom event with a missing or invalid name is ignored. Keep the name stable so your dashboard groups it correctly.

Names on built-in events are optional; an invalid optional name is omitted while the event still sends. Events restored from an older native queue are revalidated on flush: invalid legacy custom events are dropped, while invalid legacy names on built-in events are omitted. Invalid legacy currency fields are also omitted on Android; current cross-platform calls normalize currency before queuing on either platform.

Read attribution #

Once an install registers, attribution is cached on the native side. You can read it any time:

final attribution = await Postback.instance.getAttribution();
final postbackId = await Postback.instance.getPostbackId();

AttributionResult.source is one of apple_ads, tracking_link, or organic.

For revenue webhooks, set only the postbackId subscriber/user attribute. Do not forward the full getAttributionParams() map to RevenueCat; it contains attribution details such as source and isAttributed for diagnostics and custom integrations.

final postbackId = await Postback.instance.getPostbackId();
if (postbackId != null) {
  await Purchases.setAttributes({'postbackId': postbackId});
}

Manual refresh #

If you need the latest server-side resolution, call refreshAttribution():

final updated = await Postback.instance.refreshAttribution();
debugPrint('source = ${updated?.source}');

Privacy on iOS #

The production iOS SDK does not import AppTrackingTransparency, inspect ATT status, request ATT permission, or collect IDFA/IDFV. Do not add NSUserTrackingUsageDescription solely for Postback. Apple Ads attribution uses Apple's privacy-preserving AdServices token.

Production install registration includes the Postback install identifier, install lifecycle classification, SDK/platform, OS and app versions, Apple AdServices token when enabled, and optional Google Ads consent. It omits the experimental fingerprint bundle: WebView user agent, exact device/hardware data, screen metrics, CPU/memory, battery/power, languages/locale/timezone, GPU, network/VPN diagnostics, appearance, and carrier/SIM data. There is no public runtime switch that enables those probes.

Google Advertising ID (Android only) #

The native Android SDK reads GAID during install registration, off the main thread, honoring Limit Ad Tracking and dropping the all-zero ID. The plugin declares INTERNET, ACCESS_NETWORK_STATE, and com.google.android.gms.permission.AD_ID. If your app cannot collect advertising IDs (children's apps, regional policies), remove the permission in android/app/src/main/AndroidManifest.xml:

<manifest xmlns:tools="http://schemas.android.com/tools" ...>
    <uses-permission
        android:name="com.google.android.gms.permission.AD_ID"
        tools:node="remove" />
</manifest>

What happens behind the scenes #

  • configure() resolves after local-state restore. Install registration runs in the background and retries with backoff on transient failures.
  • Events queue locally on native storage and survive app restarts.
  • iOS fails fast on connectivity errors and retries through the SDK queue, so blocked or offline requests surface real errors instead of sitting in an OS connectivity wait.
  • A rejected API key (401 or 403) disables the SDK. Future events drop until clearData() is called.
  • Late identity updates (setCustomerUserId, iOS Apple Ads opt-in) retry automatically on the next configure() or foreground. If the cached install is no longer recognized, the SDK self-heals by re-registering.

Privacy #

The vendored iOS framework ships a PrivacyInfo.xcprivacy manifest declaring UserDefaults access plus linked, non-tracking DeviceID, ProductInteraction, UserID, and OtherDataTypes collection. NSPrivacyTracking is false, and the SDK does not declare a tracking-domain list. Reflect this usage in the host app's App Privacy answers.

For Android, include advertising ID collection, device IDs, approximate location/network-derived country, device or other identifiers, app activity, and (if you set customerUserId) user ID in your Play Console Data safety answers.

Don't pass raw PII through params or customerUserId. Both persist to native storage for retry durability. Use hashed or opaque identifiers instead (SHA-256 of an email, RevenueCat or Superwall app_user_id, your internal user UUID).

Local development #

await Postback.instance.configure(
  const PostbackConfig(
    apiKey: 'YOUR_DEV_KEY',
    apiUrl: 'http://localhost:3000',
    isDebug: true,
  ),
);

On Android emulator, use http://10.0.2.2:3000 to reach the host machine's localhost.

isDebug: true raises native log level to debug. iOS logs flow into Console.app; Android logs flow into logcat under the Postback tag.

Public API reference #

Postback #

import 'package:postback_flutter/postback_flutter.dart';
  • Postback.instance.configure(config) initializes the SDK.
  • sendEvent(eventType, {name, params}) enqueues an event.
  • flush() drains the queue immediately.
  • refreshAttribution() fetches the latest attribution from the backend.
  • setCustomerUserId(userId) updates the customer user ID.
  • getAttribution() returns the cached attribution.
  • getAttributionParams() returns a flat attribution/debug payload for custom integrations.
  • getPostbackId() returns the SDK install identifier.
  • enableAppleAdsAttribution() re-enables Apple Ads at runtime on iOS; returns false on Android.
  • sendTestEvent() posts a diagnostic event and resolves to { success, message }. If the backend says the cached install no longer exists, the native SDK re-registers once and retries the test event once; it never loops.
  • isInitialized() reports whether configure() resolved.
  • isSdkDisabled() reports whether a rejected API key disabled the SDK.
  • clearData() wipes local state.
  • destroy() removes native lifecycle observers.

PostbackNative #

import 'package:postback_flutter/postback_flutter.dart';
  • getDeviceInfo() on production iOS returns typed install lifecycle (InstallType.freshInstall, reinstall, appUpdate, sdkAddedOnUpdate, restore, or unknown) plus safe SDK, app, and OS metadata. High-entropy fields remain nullable in the shared type for source and Android compatibility but are omitted on iOS. Android can return its documented device/network diagnostics, carrier/SIM metadata, GAID, and Play Install Referrer metadata when available.
  • getWebViewUserAgent() returns null on production iOS; Android may return its SDK WebView user agent.
  • getAdServicesToken() returns Apple's AdServices token on iOS; null on Android.

Support #

Issues and feature requests on the GitHub repo. Direct support at support@postback.sh.

License #

MIT