Skip to content

Latest commit

 

History

History
1827 lines (1443 loc) · 66.6 KB

api-reference.md

File metadata and controls

1827 lines (1443 loc) · 66.6 KB

API

Types

Methods

Call these methods from the main isolate. From a background isolate that has not been initialized with BackgroundIsolateBinaryMessenger.ensureInitialized(rootIsolateToken), the call throws AppsFlyerException — see Getting started → Background isolates.


Platform-only APIs

Some methods below are marked (Android only) or (iOS only) because only one native SDK implements them.

Calling one on the other platform throws an AppsFlyerException. The plugin does not keep its own list of which platform supports what — every call is forwarded and the native layer answers, so the API surface stays correct as the native SDKs change. The exception's code therefore comes from the native layer, which reports 404 for a method it does not implement on both platforms. Match on the platform-only marker in this document rather than on the code.

Guard these calls with Platform.isAndroid / Platform.isIOS, or let the exception propagate if the call is not essential on that platform.


Multi-engine hosts

The native AppsFlyer SDK is process-scoped. The Flutter plugin keeps one native bridge, one event delivery subscription, and one native listener reference per event type for the whole process — the same contract as the native SDKs themselves.

Each FlutterEngine loads its own plugin instance and Dart isolate. AppsFlyerSdk.instance is a singleton within that isolate only.

Sequential engine lifecycle (one engine destroyed, another created later) is supported: re-run register*Listener() after the new engine attaches. Native configuration survives; you are reconnecting Dart callbacks to the existing bridge. See Getting started → Add-to-app and multiple Flutter engines.

Concurrent multi-engine hosts (two or more engines alive at once — add-to-app with overlapping Flutter routes, FlutterEngineGroup, multi-scene) are not supported for event delivery:

  • The engine whose EventChannel subscription attached most recently is the only one that receives conversion, deep-link, and session-ready callbacks.
  • The last register*Listener() call from any engine wins at the native SDK and replaces earlier registrations.
  • init() and start() must be driven from one primary engine; all engines share the same native SDK instance.

Integrate AppsFlyer from a single Flutter entry point. Do not call init() or register listeners from secondary engines that may run in parallel.


AppsFlyerSdk is the cross-platform SDK entry point. Use its shared instance; configuration is exposed through explicit methods.

Example:

import 'package:appsflyer_sdk/appsflyer_sdk.dart';

final AppsFlyerSdk appsflyerSdk = AppsFlyerSdk.instance;

Once the instance is obtained, call init.


an enumeration that includes the supported mediation networks by AppsFlyer.

networks
ironSource
applovinMax
googleAdMob
fyber
appodeal
admost
topon
tradplus
yandex
chartboost
unity
toponPte
customMediation
directMonetizationNetwork

An enumeration of the Android SDK logging levels: none, error, warning, info, debug, and verbose.


Contains a DeepLinkStatus, optional DeepLink, and optional DeepLinkFailure. Android supplies a stable failure type; iOS supplies a message.

The deep-link resolution status: found, notFound, error, or unknown.

Contains an optional Android error type or optional iOS error message.

Provides the full clickEvent map, getStringValue(String key), and typed getters for common Unified Deep Linking values such as deepLinkValue, mediaSource, campaign, campaignId, afSub1 through afSub5, and isDeferred. isDeferred is populated on both Android and iOS: it is true for a link resolved through deferred deep linking (first launch after install) and false for a direct click into an installed app.

Contains an optional numeric error code and message. Native failures can use HTTP-style codes (400, 404, 422, 500, …). When the platform supplies a non-numeric code, code is null and [message] carries the failure text. Plugin transport failures such as SERIALIZATION_ERROR and malformed native response payloads use non-numeric codes, so [code] is null. On iOS, SERIALIZATION_ERROR is raised when a request payload cannot be encoded as JSON (for example non-finite double values in eventValues). Android does not use that code today — the same payload may surface as UNEXPECTED_ERROR or a numeric native error instead.

Calling a platform-only API on the wrong platform throws AppsFlyerException. The plugin forwards every call to the native layer, which reports 404 for an unimplemented method on both platforms. 422 means the method exists but rejected its parameters. Match on the platform-only marker in this document rather than on the code. See Platform-only APIs.


Initializes the native SDK without sending a session. appId is the Apple App ID required by the native iOS SDK. It is optional and is not sent to the native SDK on Android.

Invalid devKey or appId values are validated by the native layer and reported as AppsFlyerException (typically code 422) when the native layer rejects the request.

Example:

import 'package:appsflyer_sdk/appsflyer_sdk.dart';
//..

final appsflyerSdk = AppsFlyerSdk.instance;
await appsflyerSdk.init(
  devKey: '<DEV_KEY>',
  appId: '<APP_ID>',
);

Register conversion, deep-link, and session-ready listeners explicitly with their corresponding registration methods.


Future<void> enableDebug(bool enabled)

Enables or disables debug logging, in the native SDK and in the plugin alike. Enable it only for development and troubleshooting. May be called before init; call before start so the first session uses the selected setting.

In release builds the plugin's Dart-side diagnostics are printed only after enableDebug(true). Debug builds always print them. The setting applies to the isolate that calls it. It does not gate the plugin's native error logging, which is always written to logcat on Android and to the system log on iOS.

await appsflyerSdk.enableDebug(true);

Future<void> setLogLevel(AFLogLevel logLevel) — Android only

Sets the Android SDK logging level. On iOS the call throws an AppsFlyerException — see Platform-only APIs. Use enableDebug for a cross-platform debug toggle.

await appsflyerSdk.setLogLevel(AFLogLevel.debug);

Future<void> registerConversionListener({OnConversionDataSuccess? onConversionDataSuccess, OnConversionDataFailure? onConversionDataFail})

Registers the native conversion-data listener and the callbacks that receive its results.

Parameter Type Description
onConversionDataSuccess void Function(Map<String, dynamic> data)? Optional. Receives the conversion data (GCD).
onConversionDataFail void Function(Map<String, dynamic> error)? Optional. Receives conversion-data retrieval failures.

await appsflyerSdk.registerConversionListener(
  onConversionDataSuccess: (data) {
    print("conversion data: $data");
  },
  onConversionDataFail: (error) {
    print("conversion data failed: $error");
  },
);

The plugin holds one callback per event and replaces it when registerConversionListener() is called again, matching the native SDKs. There is no stream to subscribe to, so one native event can never be delivered to two places in your app.

Every register*Listener() and unregister*Listener() call is atomic: if it throws, the callbacks you had before are still installed and still receiving events, so a failed re-registration never leaves your app without a listener.

onConversionDataFail is independent of the registration result: this call can succeed while the native SDK still fails to retrieve conversion data. The failure payload shape differs by platform — Android reports {"error": String} with no error code; iOS reports {"error": String, "code": int}. Android also exposes unregisterConversionListener() to remove its native listener; iOS has no corresponding unregister operation.


Future<void> unregisterConversionListener() — Android only

Unregisters the native Android conversion-data listener and drops the callbacks passed to registerConversionListener(). Call registerConversionListener() again to resume receiving conversion-data events. On iOS it throws an AppsFlyerException — see Platform-only APIs. A failed call leaves the callbacks in place, so nothing changes.

await appsflyerSdk.unregisterConversionListener();

Future<void> registerDeepLinkListener({OnDeepLinkReceived? onDeepLinking})

Registers the native Unified Deep Linking listener and the callback that receives its results.

Parameter Type Description
onDeepLinking void Function(DeepLinkResult result)? Optional. Receives every resolved deep link, deferred or direct. Omit it to register the native listener without a handler.

await appsflyerSdk.registerDeepLinkListener(onDeepLinking: (result) {
  print("result: $result");
});

Call this before init. On Android, init() hands the launch intent to the native SDK, which decides once per install whether to send the deferred deep-link resolution request; registering afterwards means that request is never sent for that install, and the skipped state persists across launches. Direct links are unaffected. Registration before init() is supported on both platforms.

Calling this again replaces the callback.


Future<void> unregisterDeepLinkListener() — Android only

Requests that Android stop forwarding Unified Deep Linking events and drops the callback passed to registerDeepLinkListener(). In the current Android integration, subsequent events may still be delivered; do not rely on this method to disable deep-link handling. On iOS it throws an AppsFlyerException — see Platform-only APIs. A failed call leaves the callback in place, so nothing changes.

await appsflyerSdk.unregisterDeepLinkListener();

In SDK 7, init(...) only initializes the SDK; it does not send a session (Launch). Call start() to report one — initialization never triggers a session automatically.

When awaitResponse is true, the Future completes when the native request succeeds and throws AppsFlyerException when it fails. Android times out after 5 seconds, iOS after 10. A timeout does not cancel the native request, which may still succeed later.

When awaitResponse is false (default), the Future completes when the native SDK accepts the request. Delivery success or failure is not reported.

parameter type description
awaitResponse bool Optional. Defaults to false. When true, wait for the native request callback. When false, return when the native SDK accepts the request.

start() must be called once per foreground cycle. The native SDK resets its "started" state every time the app is backgrounded, so a single start() at launch reports only the first session — subsequent foregrounds send nothing. Register the session-ready listener, whose callback fires once per foreground cycle when the native SDK's session-readiness conditions are satisfied:

// Recommended SDK 7 pattern: start on every session-ready signal.
await appsflyerSdk.registerSessionReadyListener(() async {
  await appsflyerSdk.start();
});

Gate the first session by deferring the start() call inside the callback.


Future<String?> getAttributionId() — returns the Facebook (Katana) attribution ID the SDK reads from the installed Facebook app's on-device content provider (also attached to attribution payloads automatically). Most apps never need it directly; exposed for parity with the native SDK. Android only — calling it on iOS throws AppsFlyerException when the native layer reports the method as unavailable.

Example:

appsFlyerSdk.getAttributionId().then((id) {
  print("Facebook attribution ID: $id");
});

  • These in-app events help you to understand how loyal users discover your app, and attribute them to specific campaigns/media-sources. Please take the time define the event/s you want to measure to allow you to send ROI (Return on Investment) and LTV (Lifetime Value).
  • The logEvent method allows you to send in-app events to AppsFlyer analytics. This method allows you to add events dynamically by adding them directly to the application code.
  • Result reporting mirrors start; both accept awaitResponse and default to fire-and-forget acceptance by the native SDK. With awaitResponse: true, Android times out after 5 seconds, iOS after 10.
parameter type description
eventName String Use descriptive, action-based names (e.g., "purchase", "add_to_cart", "level_completed"), keep names concise but meaningful, use lowercase with underscores for consistency and avoid special characters and spaces. See the recommended event list by business.
eventValues Map<String, dynamic>? Optional named event details. Values must be JSON-serializable. Non-finite numbers (double.nan, double.infinity) are rejected on iOS with AppsFlyerException (SERIALIZATION_ERROR, code is null). On Android the same payload may fail with a different code (UNEXPECTED_ERROR or a numeric native error). Validate with value.isFinite before sending.
awaitResponse bool Optional named parameter. Defaults to false. When true, wait for the native request callback. When false, return when the native SDK accepts the request.

Example:

try {
  await appsflyerSdk.logEvent(
    eventName,
    eventValues: eventValues,
    awaitResponse: true,
  );
  print("logEvent success");
} on AppsFlyerException catch (error) {
  print("logEvent error: $error");
}

// Fire-and-forget:
await appsflyerSdk.logEvent(
  eventName,
  eventValues: eventValues,
  awaitResponse: false,
);

Manually logs the device location for the current user. The Future completes when the native SDK accepts the fire-and-forget call. Supported on Android and iOS. latitude must be between -90 and 90, and longitude must be between -180 and 180.

await appsflyerSdk.logLocation(
  latitude: 32.0853,
  longitude: 34.7818,
);

Manually logs a session on Android. For typical Flutter apps, call start from the registerSessionReadyListener callback instead. Android only; on iOS the call throws an AppsFlyerException — see Platform-only APIs.

await appsflyerSdk.logSession();

Collects the open/web referrer from the launcher activity's intent. Call it before start so the referrer reaches the first session. Android only; on iOS the call throws an AppsFlyerException — see Platform-only APIs.

Requires an attached activity. When the plugin has none — for example in a headless or background engine — the native layer rejects the call with AppsFlyerException (code 422).

await appsflyerSdk.collectDataFromLauncherActivity();

SDK 7 APIs

Session readiness (SDK 7 session model)

In SDK 7 the plugin initializes on init; a session is sent when the app calls start. Because the native SDK requires start() once per foreground cycle, register the session-ready listener and call start() from its callback.

Future<void> registerSessionReadyListener(OnSessionReady onReady)

Enables the native readiness event and registers its callback.

Parameter Type Description
onReady void Function() Required. Called once per foreground cycle when the native session-readiness conditions are satisfied.

These conditions can include bounded launch deep-link processing. Call start() from the callback so every foreground reports a session.

await appsflyerSdk.registerSessionReadyListener(() async {
  await appsflyerSdk.start();
});

Calling this again replaces the callback, so start() is never issued twice for one readiness event.

Platform support: Android ✓ · iOS ✓.

Future<void> unregisterSessionReadyListener() — removes the native readiness listener and drops its callback (Android ✓ · iOS ✓).

Future<bool> isSessionReady() — returns whether all session-readiness conditions are currently satisfied. Supported on both platforms.

await appsFlyerSdk.registerSessionReadyListener(() => print("session ready"));
final ready = await appsFlyerSdk.isSessionReady();

Setter persistence (SDK 7)

The runtime/session configuration setters listed below are not a substitute for persistent application configuration. Re-apply them when the application process starts. Individual APIs can have different storage behavior; for example, Android persists the custom value supplied through setInstallId.

Re-apply your configuration setters on every cold start, before start, so they attach to that launch event:

// Runs on every cold start (e.g. from initState), before the first start().
await appsFlyerSdk.setCustomerUserId("user-42");
await appsFlyerSdk.setCurrencyCode("EUR");
await appsFlyerSdk.setAdditionalData({"tenant": "eu"});
await appsFlyerSdk.setConsentData(
  isUserSubjectToGDPR: false,
  hasConsentForDataUsage: false,
  hasConsentForAdsPersonalization: false,
  hasConsentForAdStorage: false,
);

Within a running process the values persist across background→foreground, so you only re-apply them once per cold start — not on every start(). Setters that follow this rule include setCustomerUserId, setCurrencyCode, setAdditionalData, setConsentData, anonymizeUser, setSharingFilterForPartners, setHost, and the hashed-PII setters.

Hashed PII

The email, phone, first-name, and last-name setters normalize and hash (SHA-256) their values on-device before sending them to AppsFlyer. The Facebook App-Scoped ID is numeric and is not hashed. These APIs are supported on Android and iOS.

API Description
setUserEmail(String email) Hash the user's email
setUserPhone(String countryCode, String phoneNumber) Hash the user's phone number
setUserFirstName(String firstName) Hash the user's first name
setUserLastName(String lastName) Hash the user's last name
setUserFbLoginId(int fbLoginId) Set the numeric Facebook App-Scoped ID
clearUserPii() Clear all previously set PII (hashed email/phone/name fields + the fb login id)
await appsFlyerSdk.setUserEmail("a@a.com");
await appsFlyerSdk.setUserPhone("1", "5551234567");
await appsFlyerSdk.clearUserPii();

Other functionalities:

Future<void> anonymizeUser(bool shouldAnonymize)

It is possible to anonymize specific user identifiers within AppsFlyer analytics.
This complies with both the latest privacy requirements (GDPR, COPPA) and Facebook's data and privacy policies. To anonymize an app user.

parameter type description
shouldAnonymize boolean True if want Anonymize user Data (default value is false).

Example:

await appsFlyerSdk.anonymizeUser(true);

Future<void> setMinTimeBetweenSessions(int seconds) You can set the minimum time between session (the default is 5 seconds)

await appsFlyerSdk.setMinTimeBetweenSessions(3);

Future<void> stop(bool shouldStop) You can stop sending events to Appsflyer by using this method.

Example:

await widget.appsFlyerSdk.stop(true);

Future<bool> isStopped()

Returns whether the SDK is currently stopped (see stop).

Example:

final stopped = await appsFlyerSdk.isStopped();

Future<void> setCurrencyCode(String currencyCode)

Example:

await appsFlyerSdk.setCurrencyCode("USD");

Future<void> setIsUpdate(bool isUpdate) — Android only

Example:

await appsFlyerSdk.setIsUpdate(true);

Future<void> enableTCFDataCollection(bool shouldCollect)

The enableTCFDataCollection method is employed to control the automatic collection of the Transparency and Consent Framework (TCF) data. By setting this flag to true, the system is instructed to automatically collect TCF data. Conversely, setting it to false prevents such data collection.

Example:

await appsFlyerSdk.enableTCFDataCollection(true);

Future<void> setConsentData({required bool isUserSubjectToGDPR, bool? hasConsentForDataUsage, bool? hasConsentForAdsPersonalization, bool? hasConsentForAdStorage})

Sets user consent preferences for GDPR and ad personalization

The named parameters provide manual GDPR and DMA consent data. For a complete workflow, see the DMA compliance documentation.

  1. Users subjected to GDPR:
await appsflyerSdk.setConsentData(
  isUserSubjectToGDPR: true,
  hasConsentForDataUsage: true,
  hasConsentForAdsPersonalization: true,
  hasConsentForAdStorage: true,
);
  1. Users not subject to GDPR:
await appsflyerSdk.setConsentData(
  isUserSubjectToGDPR: false,
);

The three consent fields may be omitted or null when GDPR does not apply.

When GDPR applies, supply hasConsentForDataUsage and hasConsentForAdsPersonalization before the first start().

To reflect consent in the conversion payload, configure either enableTCFDataCollection or setConsentData after initialization and before the first start():

final appsflyerSdk = AppsFlyerSdk.instance;
await appsflyerSdk.init(
  devKey: '<DEV_KEY>',
  appId: '<APP_ID>',
);

await appsflyerSdk.setConsentData(
  isUserSubjectToGDPR: true,
  hasConsentForDataUsage: true,
  hasConsentForAdsPersonalization: true,
  hasConsentForAdStorage: true,
);

await appsflyerSdk.registerSessionReadyListener(() async {
  await appsflyerSdk.start();
});

If both TCF collection and explicit consent are used, AppsFlyer backend prioritizes the explicit data supplied through setConsentData.


Future<void> setCustomerUserId(String customerId)

What is customer user id?

Example:

await appsFlyerSdk.setCustomerUserId("id");

Future<void> setAdditionalData(Map<String, dynamic> customData)

customData must be non-null; pass an empty map to clear the data.

Example:

var data = {"key1": "value1", "key2": "value2"};
await appsFlyerSdk.setAdditionalData(data);

Future<void> setCollectAndroidID(bool isCollect) — Android only

Example:

await appsFlyerSdk.setCollectAndroidID(true);

Future<void> setImeiData(String imei) — Android only

Supplies a pre-collected IMEI value to the SDK.

Example:

await appsFlyerSdk.setImeiData('your-imei-value');

Future<void> setOaidData(String oaid) — Android only

Supplies a pre-collected OAID value to the SDK.

Example:

await appsFlyerSdk.setOaidData('your-oaid-value');

Future<void> setAndroidIdData(String androidId) — Android only

Supplies a pre-collected Android ID value to the SDK.

Example:

await appsFlyerSdk.setAndroidIdData('your-android-id-value');

Future<void> setHost(String hostPrefixName, String hostName)

Changes the default AppsFlyer host. Use this method only when instructed by AppsFlyer Support.

  • Android: hostName must be non-empty. hostPrefixName may be empty.
  • iOS: both hostPrefixName and hostName must be non-empty.

Invalid arguments are reported as AppsFlyerException.

Example:

await appsFlyerSdk.setHost("pref", "my-host");

Future<String> getHostName()

Returns an empty string on iOS when setHost has not been called.

Example:

appsFlyerSdk.getHostName().then((name) {
         print("Host name: ${name}");
       });

Future<String> getHostPrefix()

Returns an empty string on iOS when setHost has not been called.

Example:

appsFlyerSdk.getHostPrefix().then((name) {
         print("Host prefix: ${name}");
       });

Future<void> updateServerUninstallToken(String token)

Registers a token for uninstall measurement. Pass an FCM registration token on Android or a hexadecimal APNs device token on iOS.

Token format differs per platform: on Android pass the FCM/GCM registration token as-is; on iOS pass the APNs device token hex-encoded as an even-length string (a non-hex string is rejected natively). On iOS, getAPNSToken() already returns the token in hex form.

Example:

await appsFlyerSdk.updateServerUninstallToken(token);

Validate Purchase

Cross-platform API:

Future<Map<String, dynamic>> validateAndLogInAppPurchase(AFPurchaseDetails purchase, {Map<String, String>? additionalParameters})

The unified purchase validation API works across Android and iOS. Use the purchase-details implementation for the current platform.

Parameter Type Description
purchase AFPurchaseDetails An AFAndroidPurchaseDetails or AFIOSPurchaseDetails instance
additionalParameters Map<String, String>? Optional additional parameters

Both platforms always wait for the native validation result, so the Future completes with that result or throws AppsFlyerException when validation fails. Android times out after 5 seconds, iOS after 30. When the native reply carries no payload, the Future completes with an empty map.

AFPurchaseDetails interface:

Property Type Description
purchaseType AFPurchaseType Type of purchase (oneTimePurchase or subscription)
productId String Product identifier

AFAndroidPurchaseDetails:

Property Type Description
purchaseType AFPurchaseType Type of Google Play purchase
purchaseToken String Google Play purchase token
productId String Product identifier

AFIOSPurchaseDetails:

Property Type Description
purchaseType AFPurchaseType Type of App Store purchase
transactionId String App Store transaction ID
productId String Product identifier

AFPurchaseType:

  • AFPurchaseType.oneTimePurchase - For one-time in-app purchases
  • AFPurchaseType.subscription - For subscription purchases

Example:

final AFPurchaseDetails purchaseDetails = Platform.isAndroid
    ? const AFAndroidPurchaseDetails(
        purchaseType: AFPurchaseType.oneTimePurchase,
        purchaseToken: "your_purchase_token",
        productId: "your_product_id",
      )
    : const AFIOSPurchaseDetails(
        purchaseType: AFPurchaseType.oneTimePurchase,
        transactionId: "your_transaction_id",
        productId: "your_product_id",
      );

// Validate purchase
try {
  Map<String, dynamic> result = await appsFlyerSdk.validateAndLogInAppPurchase(
    purchaseDetails,
    additionalParameters: {"custom_param": "value"},
  );
  print("Validation successful: $result");
} on AppsFlyerException catch (error) {
  print("Validation failed: $error");
} on ArgumentError catch (error) {
  // A purchase-details object was used on the wrong platform.
  print("Invalid purchase details: $error");
}

Key Benefits:

  • Cross-platform compatibility: Works on both Android and iOS with the same API
  • Type safety: Uses structured data classes instead of platform-specific parameters
  • Enhanced error handling: Provides detailed error information in structured format (including NSError details on iOS)
  • Future-proof: Built on AppsFlyer's latest V2 validation infrastructure
  • Platform mapping: Each purchase-details implementation uses the corresponding Android or iOS purchase-validation model

Purchase validation sandbox mode for iOS:

Future<void> setUseReceiptValidationSandbox(bool sandbox) — iOS only

Enables sandbox mode for App Store receipt validation.

Example:

await appsFlyerSdk.setUseReceiptValidationSandbox(true);

Future<void> setUseUninstallSandbox(bool sandbox) — iOS only

Enables sandbox mode for uninstall-measurement validation (companion of setUseReceiptValidationSandbox).

Example:

await appsFlyerSdk.setUseUninstallSandbox(true);

validateAndLogInAppPurchase

See Validate Purchase above for the full validateAndLogInAppPurchase reference — signature, AFPurchaseDetails / AFPurchaseType, example, key benefits, and the iOS sandbox toggles. This anchor is kept for existing links.


Push-notification campaigns are used to create re-engagements with existing users → Learn more here

The Android API maps directly to the native AFPushData fields. Calling it triggers a new Android Launch request even when the SDK already sent a Launch in the current session.

Passes the push-notification payload to the iOS SDK. Preserve the AppsFlyer custom-data structure from the notification; native integrations should pass the complete APNs userInfo dictionary.

Platform-Specific Requirements

🟩 Android:
Call sendPushNotificationData with campaign and pid, plus optional isRetargeting and additionalParameters values.

🍎 iOS:
Call handlePushNotification with the complete notification payload.

When using firebase_messaging, RemoteMessage.data contains only the custom data fields, not the complete APNs userInfo dictionary. Ensure that every AppsFlyer attribution field is present in that custom data. If your provider encodes a nested object as a JSON string, decode it before reading its fields.


Integration Approaches

AppsFlyer supports two approaches for measuring push notification campaigns:

Approach 1: Traditional Attribution Parameters (af object)

Use this approach when your push payload contains a custom af object with attribution parameters.

Required parameters: pid, is_retargeting, c

📦 Example Push Payload with af Object:

{
  "af": {
    "c": "test_campaign",
    "is_retargeting": true,
    "pid": "push_provider_int"
  },
  "aps": {
    "alert": "Get 5000 Coins",
    "badge": "37",
    "sound": "default"
}
}

Implementation:

Future<void> passPushToAppsFlyer(Map<String, dynamic> data) async {
  if (Platform.isAndroid) {
    final rawAf = data['af'];
    final decodedAf = rawAf is String ? jsonDecode(rawAf) : rawAf;
    final af = Map<String, dynamic>.from(decodedAf as Map);
    await appsFlyerSdk.sendPushNotificationData(
      campaign: af['c'] as String,
      pid: af['pid'] as String,
      isRetargeting: af['is_retargeting'] == true,
    );
  } else if (Platform.isIOS) {
    await appsFlyerSdk.handlePushNotification(data);
  }
}

// 1️⃣ Handle Foreground Messages
FirebaseMessaging.onMessage.listen((RemoteMessage message) async {
  await passPushToAppsFlyer(message.data);
});

// 2️⃣ Handle Notification Taps (App in Background)
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) async {
  await passPushToAppsFlyer(message.data);
});

// 3️⃣ Handle App Launch from Push (Terminated State)
Future<void> handleInitialPush() async {
  final message = await FirebaseMessaging.instance.getInitialMessage();
  if (message != null) {
    await passPushToAppsFlyer(message.data);
  }
}

Call handleInitialPush() once during app startup, after Flutter is initialized and the AppsFlyer SDK setup has completed. getInitialMessage() returns the notification that opened an app from the terminated state. A Firebase background-message handler is a separate flow and is not required to handle a notification tap that launches the app.


Approach 2: OneLink URL in Push Payload (Recommended)

Use this approach when your push payload contains a OneLink URL for deep linking. This method provides a unified deep linking experience.

⚠️ Important: This approach requires calling two different methods depending on the platform!

Step 1: Configure Deep Link Path (BOTH Platforms)

Call addPushNotificationDeepLinkPath before init() to tell AppsFlyer where to find the OneLink URL in your push payload.

await appsFlyerSdk.addPushNotificationDeepLinkPath(
  ["deeply", "nested", "deep_link"],
);
await appsFlyerSdk.registerDeepLinkListener(onDeepLinking: (result) {
  // Handle deep-link navigation here.
});
await appsFlyerSdk.init(
  devKey: '<DEV_KEY>',
  appId: '<APP_ID>',
);

Push configuration does not replace the SDK 7 session lifecycle. Complete the normal session-ready → start() setup so the initial Launch and every later foreground session are reported.

Step 2: Send Push Payload to SDK

🟩 Android:
On Android, calling addPushNotificationDeepLinkPath is sufficient. The SDK automatically extracts and processes the OneLink URL.

🍎 iOS:
On iOS, you MUST also call handlePushNotification(pushPayload) to pass the push payload to the SDK so it can extract and process the OneLink URL.

📦 Example Push Payload with OneLink URL:

{
  "deeply": {
    "nested": {
      "deep_link": "https://yourapp.onelink.me/ABC/campaign123"
    }
  },
  "aps": {
    "alert": "Check out our new feature!",
    "badge": "1",
    "sound": "default"
  }
}

Complete Implementation Example:

// ========================================
// 1. Configure SDK (in main.dart or app initialization)
// ========================================
Future<void> initializeAppsFlyer() async {
  // STEP 1: Configure the deep-link path before init().
  await appsFlyerSdk.addPushNotificationDeepLinkPath(
    ["deeply", "nested", "deep_link"],
  );

  // STEP 2: Enable native deep-link events and handle them. Also before init().
  await appsFlyerSdk.registerDeepLinkListener(
      onDeepLinking: (DeepLinkResult result) {
    if (result.status == DeepLinkStatus.found) {
      print("Deep link found: ${result.deepLink?.deepLinkValue}");
      // Handle deep-link navigation here.
    }
  });

  // STEP 3: Initialize the SDK.
  await appsFlyerSdk.init(
    devKey: '<DEV_KEY>',
    appId: '<APP_ID>',
  );

  // STEP 4: Register last because this can invoke the callback immediately.
  // Required: report a session on every foreground cycle.
  await appsFlyerSdk.registerSessionReadyListener(() async {
    try {
      await appsFlyerSdk.start(awaitResponse: true);
      print("AppsFlyer session reported.");
    } on AppsFlyerException catch (error) {
      print("AppsFlyer start error: $error");
    }
  });
}

// ========================================
// 2. Handle Push Notifications
// ========================================

// 🍎 iOS: MUST call handlePushNotification
// 🟩 Android: addPushNotificationDeepLinkPath is sufficient

// 1️⃣ Foreground Messages
FirebaseMessaging.onMessage.listen((RemoteMessage message) async {
  // iOS: Required to process OneLink URL
  if (Platform.isIOS) {
    await appsFlyerSdk.handlePushNotification(message.data);
  }
});

// 2️⃣ Background Notification Taps (App in Background)
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) async {
  // iOS: Required to process OneLink URL
  if (Platform.isIOS) {
    await appsFlyerSdk.handlePushNotification(message.data);
  }
});

// 3️⃣ App Launch from Push (Terminated State)
Future<void> handleInitialPush() async {
  final message = await FirebaseMessaging.instance.getInitialMessage();
  if (message != null) {
    // iOS: Required to process OneLink URL from terminated state
    if (Platform.isIOS) {
      await appsFlyerSdk.handlePushNotification(message.data);
    }
  }
}

Call handleInitialPush() once after initializeAppsFlyer() completes. On Android, addPushNotificationDeepLinkPath() handles the configured OneLink path; the explicit terminated-state forwarding above is required only on iOS.

Key Differences Between Approaches:

Traditional af Object OneLink URL (Recommended)
Android sendPushNotificationData(...) addPushNotificationDeepLinkPath() (auto-handles)
iOS handlePushNotification(pushPayload) addPushNotificationDeepLinkPath() + handlePushNotification(pushPayload)
Deep Linking Basic attribution only Full deep linking with the registerDeepLinkListener callback
Use Case Simple re-engagement Re-engagement + in-app navigation

Summary

  • Traditional approach: Call Android sendPushNotificationData(...) or iOS handlePushNotification(pushPayload)
  • OneLink approach (Recommended):
    • ✅ Both platforms: Call addPushNotificationDeepLinkPath() and registerDeepLinkListener() before SDK init
    • ✅ iOS only: Also call handlePushNotification(pushPayload) when push is received
    • ✅ Both platforms: Handle deep links in the registerDeepLinkListener callback

Registers a custom key path for resolving deep links inside custom JSON payloads in push notifications.

This is the recommended method of integrating AppsFlyer with push notifications. Learn more here.

⚠️ Call this method before init(). deepLinkPath must not be empty. ⚠️

Example:

await appsFlyerSdk.addPushNotificationDeepLinkPath(
  ["deeply", "nested", "deep_link"],
);

With this configuration, the SDK will extract the URL from the following push payload:

{
  "deeply": {
      "nested": {
          "deep_link": "https://yourdeeplink2.onelink.me"
      }
  }
}

User Invite

  1. First define the Onelink ID (find it in the AppsFlyer dashboard in the onelink section:

Future<void> setAppInviteOneLink(String oneLinkId)

  1. Set the AppsFlyerInviteLinkParams class to set the query params in the user invite link:

class AppsFlyerInviteLinkParams {
  final String? channel;
  final String? campaign;
  final String? referrerName;
  final String? referrerImageUrl;
  final String? referrerCustomerId;
  final String? baseDeepLink;
  final String? brandDomain;
  final Map<String, String>? userParams;
}
  1. Call the generateInviteLink API to generate the user invite link.

Future<String> generateInviteLink({AppsFlyerInviteLinkParams? parameters, bool awaitResponse = true})

The Future completes with the generated URL. Native generation failures are reported as AppsFlyerException. On Android, awaitResponse: true waits for asynchronous short-link generation and false returns the synchronously generated long link. On iOS, link generation always waits for the asynchronous result. Both platforms time out after 10 seconds.

Parameter Type Description
parameters AppsFlyerInviteLinkParams? Optional OneLink generation parameters
awaitResponse bool Optional. Defaults to true. Android honors this value; iOS always waits for completion.

Example:

await appsFlyerSdk.setAppInviteOneLink('OnelinkID');

const AppsFlyerInviteLinkParams inviteLinkParams = AppsFlyerInviteLinkParams(
      channel: "",
      referrerName: "",
      baseDeepLink: "",
      brandDomain: "",
      referrerCustomerId: "",
      referrerImageUrl: "",
      campaign: "",
      userParams: {"key":"value"}
);

final url = await appsFlyerSdk.generateInviteLink(
  parameters: inviteLinkParams,
  awaitResponse: true,
);
print(url);
  1. Log the af_invite event when the user actually shares the invite:

Future<void> logInvite(String channel, [Map<String, String>? eventParameters])

Logs the af_invite in-app event so AppsFlyer can attribute the invite and any downstream installs to the referring user. The returned Future<void> completes after the native SDK accepts the logging call. The native API does not provide a network-completion callback. Supported on Android and iOS.

Example:

await appsFlyerSdk.logInvite("facebook", {"referrerId": "user-123"});

Cross promotion

Future<void> logCrossPromoteImpression(String appId, {String campaign = '', Map<String, String>? userParams})

Records an impression for a promoted app:

await appsFlyerSdk.logCrossPromoteImpression(
  "promoted.app.id",
  campaign: "summer",
  userParams: {"source": "banner"},
);

Future<void> logAndOpenStore(String promotedAppId, {String campaign = '', Map<String, String>? userParams})

Records the promotion and asks the native SDK to open the promoted app's store page.

The returned Future means different things per platform. On Android it completes as soon as the native SDK has been invoked, without waiting for the store to open. On iOS the plugin must first obtain the click URL from the native SDK and then open it, so the Future completes only after the store has been asked to open; that click-URL step times out after 10 seconds and raises AppsFlyerException. A timeout does not cancel the native request. If iOS returns no click URL, the Future still completes successfully but nothing opens. The click URL itself is never returned to Dart.

await appsFlyerSdk.logAndOpenStore(
  "promoted.app.id",
  campaign: "summer",
  userParams: {"source": "banner"},
);

Both APIs are supported on Android and iOS.


Future<void> enableFacebookDeferredApplinks(bool isEnabled)

Please make sure the relevant Facebook dependecies are added to the project! Call this method before init().

For more information check the following article: https://support.appsflyer.com/hc/en-us/articles/207033826-Facebook-Ads-setup-guide#advanced-using-facebook-ads-appsflyer-sdks-for-deferred-deep-linking

Example:

await appsFlyerSdk.enableFacebookDeferredApplinks(true);

Future<void> setFacebookDeferredAppLink(String? url) (iOS only)

Manually sets — or, with null, clears — the Facebook deferred app-link URL. On Android the call throws an AppsFlyerException — see Platform-only APIs.

Use this only when you already hold the deferred link and want to skip the Facebook SDK lookup; otherwise prefer enableFacebookDeferredApplinks(true).

Example:

await appsFlyerSdk.setFacebookDeferredAppLink(
  "https://myapp.onelink.me/abc123",
);

Future<void> setDisableSKAdNetwork(bool disable) — iOS only

Use this API in order to disable the SK Ad network (request will be sent but the rules won't be returned). On Android the call throws an AppsFlyerException — see Platform-only APIs.

Example:

await appsFlyerSdk.setDisableSKAdNetwork(true);

Future<void> setDisableAppleAdsAttribution(bool disable) — iOS only

Disables Apple Ads (Apple Search Ads) attribution via the AdServices framework — pass true to stop the SDK from calling AAAttribution.attributionToken (iOS 14.3+). On Android the call throws an AppsFlyerException — see Platform-only APIs.

Example:

if (Platform.isIOS) {
  await appsFlyerSdk.setDisableAppleAdsAttribution(true);
}

Future<void> setDisableIDFVCollection(bool disable) — iOS only

Disables collection of the IDFV (Identifier for Vendor) — pass true to stop the SDK from collecting it. Set it before start(). On Android the call throws an AppsFlyerException — see Platform-only APIs.

Example:

if (Platform.isIOS) {
  await appsFlyerSdk.setDisableIDFVCollection(true);
}

Future<void> setShouldCollectDeviceName(bool collect) — iOS only

Enables collection of the device name (e.g. "John's iPhone"). This is an opt-in — collection is off by default and the device name is personal data (PII), so only enable it if your privacy policy covers it. Pass true to start collecting it. On Android the call throws an AppsFlyerException — see Platform-only APIs.

Example:

if (Platform.isIOS) {
  await appsFlyerSdk.setShouldCollectDeviceName(true);
}

Future<String?> getAppsFlyerUID()

Use this API in order to get the AppsFlyer ID.

Example:

appsFlyerSdk.getAppsFlyerUID().then((AppsFlyerId) {
  print("AppsFlyer ID: ${AppsFlyerId}");
});

Future<String> getSdkVersion()

Returns the native AppsFlyer SDK version.

final sdkVersion = await appsFlyerSdk.getSdkVersion();

String get pluginVersion

Returns the Flutter plugin version without invoking a native method.

print(appsFlyerSdk.pluginVersion);

Future<bool> isPreInstalledApp() — Android only

Returns whether the app install was a device preinstall (OEM/manufacturer). On iOS the native layer reports the method as unavailable and the call throws AppsFlyerException. See also setPreinstallAttribution.

Example:

final bool preinstalled = await appsFlyerSdk.isPreInstalledApp();

Future<void> setCurrentDeviceLanguage(String language) — iOS only

Use this API in order to set the language

Example:

await appsFlyerSdk.setCurrentDeviceLanguage("en");

Future<void> setInstallId(String installId)

Sets a unique install id for the app installation, letting you correlate the AppsFlyer install with an id you generate yourself (e.g. for server-side reconciliation). Supported on both platforms, but the call order and setup requirements differ:

  • iOS: call this before init() (before the dev key is set). Requires AppsFlyerAllowCustomInstallId set to YES in Info.plist.
  • Android: call this after init(). Requires the <meta-data> flag APPSFLYER_ALLOW_CUSTOM_INSTALL_ID set to true in AndroidManifest.xml.

On both platforms, the call is silently ignored — no error is returned — if the corresponding manifest/plist flag is missing.

Example:

await appsFlyerSdk.setInstallId("install-123");

Future<void> setPreinstallAttribution(String mediaSource, {String campaign = '', String siteId = ''})

Attributes the install to a device preinstall (OEM / manufacturer) deal, declaring that the app shipped preinstalled and attributing the install to the given mediaSource, campaign, and siteId. Call it before start().

Android only — the iOS SDK does not provide this programmatic preinstall-attribution API. On iOS the call throws an AppsFlyerException — see Platform-only APIs.

Example:

await appsFlyerSdk.setPreinstallAttribution(
  "media_source",
  campaign: "campaign",
  siteId: "site_id",
);

Future<void> setAppId(String appId)

Overrides the app ID reported to AppsFlyer. Call it before start(). The Android SDK rejects an empty appId, and the returned Future throws AppsFlyerException.

Android only — on iOS the app ID is provided through init() and the iOS SDK has no setAppId, so the call throws an AppsFlyerException — see Platform-only APIs.

Example:

await appsFlyerSdk.setAppId("com.example.app");

Future<void> setSharingFilterForPartners(List<String>? partners)

Used by advertisers to exclude specified networks/integrated partners from getting data. Learn more here

Example:

await appsFlyerSdk.setSharingFilterForPartners(['facebook_int']);
await appsFlyerSdk.setSharingFilterForPartners(
  ['facebook_int', 'googleadwords_int'],
);

Passing null or an empty list clears the filter; the plugin normalizes both to the same native request, so the two are interchangeable and clear the filter on Android and iOS alike.


Future<void> setOneLinkCustomDomain(List<String> domains)

Use this API in order to set branded domains. domains must not be empty.

Find more information in the following article on branded domains.

Example:

await appsFlyerSdk.setOneLinkCustomDomain(
  ["promotion.greatapp.com", "click.greatapp.com", "deals.greatapp.com"],
);

Future<void> setDisableAdvertisingIdentifiers(bool disable)

Disables collection of advertising identifiers (GAID / IDFA / OAID). Pass true to disable collection (enabled by default).

On iOS, this is a runtime setting only. It does not select the Strict vs Main native SDK variant or change the embedded App Store privacy manifest. Kids apps and other ad-ID-less integrations that require the Strict binary must set $AppsFlyerStrictMode = true in the app Podfile before pod install — see Installation — Strict mode.

Example:

await appsFlyerSdk.setDisableAdvertisingIdentifiers(true);

Future<void> setDisableCollectASA(bool disable) — iOS only

Controls collection of Apple Search Ads attribution data. On Android the call throws an AppsFlyerException — see Platform-only APIs.

if (Platform.isIOS) {
  await appsFlyerSdk.setDisableCollectASA(true);
}

Future<void> setPartnerData(String partnerId, Map<String, dynamic> data)

Allows sending custom data for partner integration purposes.

Example:

final partnerData = <String, dynamic>{"puid": "1234", "region": "eu"};
await appsFlyerSdk.setPartnerData("partnerId", partnerData);

Future<void> setResolveDeepLinkURLs(List<String> urls)

Advertisers can wrap an AppsFlyer OneLink within another Universal Link. This Universal Link will invoke the app but any deep linking data will not propagate to AppsFlyer.

setResolveDeepLinkURLs enables you to configure the SDK to resolve the wrapped OneLink URLs, so that deep linking can occur correctly.

urls must not be empty.

Example:

await appsFlyerSdk.setResolveDeepLinkURLs(
  ["clickdomain.com", "myclickdomain.com", "anotherclickdomain.com"],
);

Future<void> setOutOfStore(String sourceName)

Android Only!

Specify the alternative app store that the app is downloaded from (out-of-store attribution). Re-apply on every cold start — SDK 7 does not persist setter values.

This API does not register manifest receivers. See Advanced features — Android Out of Store.

Example:

  if (Platform.isAndroid) {
    await appsFlyerSdk.setOutOfStore("amazon");
  }

Future<String?> getOutOfStore()

Android Only!

Gets the configured alternative app-store value. If no runtime value was set, the Android SDK may return the AF_STORE manifest value.

Example:

  if (Platform.isAndroid) {
    final store = await appsFlyerSdk.getOutOfStore();
    print(store);
  }

Future<void> setDisableNetworkData(bool isDisable)

Android Only!

Use to opt-out of collecting the network operator name (carrier) and sim operator name from the device.

Example:

  if (Platform.isAndroid) {
    await appsFlyerSdk.setDisableNetworkData(true);
  }

Future<void> disableAppSetId()

Android Only!

Disables the native Android SDK's automatic AppSet ID collection. Use this method to opt out of AppSet ID collection for privacy compliance.

Example:

  if (Platform.isAndroid) {
    await appsFlyerSdk.disableAppSetId();
  }

Future<void> performDeepLinking(String url, {bool shouldTriggerSession = false})

Manually triggers deep link resolution for a given url (full URL, OneLink, or intent-data string). Use it to resolve a deep link before the SDK starts (e.g. when delaying start()), or for links that don't arrive through the standard intent / Universal Link flow (e.g. Firebase Messaging).

The resolved link is delivered to the registerDeepLinkListener callback on both platforms. shouldTriggerSession defaults to false, so a bare performDeepLinking(url) resolves the link without an extra Launch and behaves identically on Android and iOS. The flag is Android-only: pass true to also enqueue a Launch for re-engagement; on iOS it has no effect because the link is always resolved without an extra managed session.

Future<void> configureAppsFlyer() async {
  final appsflyerSdk = AppsFlyerSdk.instance;

  await appsflyerSdk.registerDeepLinkListener(
      onDeepLinking: (DeepLinkResult result) {
    switch (result.status) {
      case DeepLinkStatus.found:
        print(result.deepLink);
        print("deep link value: ${result.deepLink?.deepLinkValue}");
        break;
      case DeepLinkStatus.notFound:
        print("deep link not found");
        break;
      case DeepLinkStatus.error:
        print("deep link error: ${result.error}");
        break;
      case DeepLinkStatus.unknown:
        print("unknown deep link status");
        break;
    }
  });

  await appsflyerSdk.init(
    devKey: '<DEV_KEY>',
    appId: '<APP_ID>',
  );

  await appsflyerSdk.registerConversionListener(
    onConversionDataSuccess: (data) {
      print("conversion data: $data");
    },
  );

  // Resolve a deep link manually.
  await appsflyerSdk.performDeepLinking(
    "https://yourapp.onelink.me/abc123",
  );
}

Future<void> appendParametersToDeepLinkingURL(String contains, Map<String, String> parameters)

Appends parameters to any deep-link URL that contains the contains substring, before the SDK resolves / attributes it. Useful for enriching wrapped OneLinks with extra query parameters. Implemented on both Android and iOS.

Pass a non-empty contains and at least one entry in parameters. An empty contains is invalid on both platforms, and an empty parameters map is invalid on iOS.

await appsFlyerSdk.appendParametersToDeepLinkingURL(
  "deeplink",
  {"deep_link_sub1": "cat123", "deep_link_value": "shoes"},
);

Future<void> setDeepLinkTimeout(int timeout)

Sets the deep-link resolution timeout, in milliseconds. Configure it before init(). Use a positive value for a cross-platform configuration. The default when unset differs by platform: 3000 ms on Android, 60000 ms on iOS.

  await appsFlyerSdk.setDeepLinkTimeout(3000);

The logAdRevenue API is designed to simplify the process of logging ad revenue events to AppsFlyer from your Flutter application. This API tracks revenue generated from advertisements, enriching your monetization analytics. Below you will find instructions on how to use this API correctly, along with detailed descriptions and examples for various input scenarios.

Usage:

To use the logAdRevenue method, you must:

  1. Prepare the required information about the ad revenue event.
  2. Pass the values to logAdRevenue.

Parameters The method accepts the following ad revenue values:

  • monetizationNetwork: The source network from which the revenue was generated (e.g., AdMob, Unity Ads).
  • mediationNetwork: The mediation platform managing the ad (use AFMediationNetwork enum for supported networks).
  • currencyIso4217Code: The ISO 4217 currency code representing the currency of the revenue amount (e.g., "USD", "EUR").
  • revenue: The amount of revenue generated from the ad.
  • additionalParameters: Additional parameters related to the ad revenue event (optional).

AFMediationNetwork Enum AFMediationNetwork is an enumeration that includes the supported mediation networks by AppsFlyer. It's important to use this enum to ensure you provide a valid network identifier to the logAdRevenue API.

Note (behavior): The returned Future completes after the plugin validates the request and invokes the native logging API. Validation and native call failures are surfaced as AppsFlyerException. The native API has no delivery callback, so completion does not confirm that the event was uploaded.

Cross-platform note: The plugin maps AFMediationNetwork.customMediation and AFMediationNetwork.directMonetizationNetwork to the correct platform value automatically. All public enum values work on both platforms; no caller action is needed.

Example:

// Log the ad revenue event.
await appsFlyerSdk.logAdRevenue(
  monetizationNetwork: "GoogleAdMob", // Replace with your actual monetization network.
  mediationNetwork: AFMediationNetwork.applovinMax, // Use the value from the enum.
  currencyIso4217Code: "USD", 
  revenue: 1.23,
  additionalParameters: {
    // Optional additional parameters can be added here. This is an example, can be discard if not needed.
    'adUnitId': 'ca-app-pub-XXXX/YYYY', 
    'ad_network_click_id': '12345'
  }
);

Additional Points

  • Mediation network input must be from the provided AFMediationNetwork enum to ensure proper processing by AppsFlyer. For instance, use AFMediationNetwork.googleAdMob to denote Google AdMob as the Mediation Network.
  • The additionalParameters map is optional. Use it to pass any extra information you have regarding the ad revenue event; this information could be useful for more refined analytics.
  • Make sure the currencyIso4217Code adheres to the appropriate standard. Misconfigured currency code may result in incorrect revenue tracking.