> ## Documentation Index
> Fetch the complete documentation index at: https://docs.appdna.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Flutter API Reference

> Complete method reference for appdna_sdk

<Info>
  **Supported on:** iOS SDK `1.0.61+` · Android SDK `1.0.33+` · Flutter SDK `1.0.3+`
</Info>

This is the complete reference for all public methods, properties, types, and delegate classes in the `appdna_sdk` Flutter package. The Flutter SDK delegates rendering, business logic, and storage to the native iOS and Android layers, so every method here resolves to native execution under the hood.

## Core Methods

All core methods are static on the `AppDNA` class.

| Method          | Signature                                                                                                | Description                                                                                     |
| --------------- | -------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| `configure`     | `Future<void> AppDNA.configure({required String apiKey, AppDNAEnvironment env, AppDNAOptions? options})` | Initialize the SDK. Call once at app startup.                                                   |
| `identify`      | `Future<void> AppDNA.identify(String userId, {Map<String, dynamic>? traits})`                            | Associate events with a user identity and optional traits.                                      |
| `reset`         | `Future<void> AppDNA.reset()`                                                                            | Clear user identity, experiment exposures, and in-session state. Call on logout.                |
| `track`         | `Future<void> AppDNA.track(String event, {Map<String, dynamic>? properties})`                            | Track a named event with optional properties.                                                   |
| `flush`         | `Future<void> AppDNA.flush()`                                                                            | Force an immediate flush of all queued events.                                                  |
| `onReady`       | `Future<void> AppDNA.onReady(void Function() callback)`                                                  | Invoke `callback` once the SDK is initialized and remote config is loaded.                      |
| `shutdown`      | `Future<void> AppDNA.shutdown()`                                                                         | Shut down the SDK, flush events, and release resources. No-op on iOS.                           |
| `setLogLevel`   | `void AppDNA.setLogLevel(String level)`                                                                  | Change log verbosity at runtime. Accepts `"none"`, `"error"`, `"warning"`, `"info"`, `"debug"`. |
| `setConsent`    | `Future<void> AppDNA.setConsent({required bool analytics})`                                              | Set analytics consent. When `false`, events are dropped silently.                               |
| `getSdkVersion` | `Future<String> AppDNA.getSdkVersion()`                                                                  | Get the native SDK version string (e.g., iOS `"1.0.69"` or Android `"1.0.41"`).                 |

## Config Methods

| Method               | Signature                                            | Description                                                    |
| -------------------- | ---------------------------------------------------- | -------------------------------------------------------------- |
| `getRemoteConfig`    | `Future<dynamic> AppDNA.getRemoteConfig(String key)` | Get a remote config value by key.                              |
| `isFeatureEnabled`   | `Future<bool> AppDNA.isFeatureEnabled(String flag)`  | Check whether a feature flag is enabled.                       |
| `forceRefreshConfig` | `Future<void> AppDNA.forceRefreshConfig()`           | Force-refresh the remote config from Firestore, bypassing TTL. |

## Session Data Methods

Cross-module key-value bag used by Console-configured template interpolation (`{{session.*}}`).

| Method             | Signature                                                                | Description                                                                                                                                                                                                                      |
| ------------------ | ------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `setSessionData`   | `Future<void> AppDNA.setSessionData(String key, Object value)`           | Store a session value.                                                                                                                                                                                                           |
| `getSessionData`   | `Future<dynamic> AppDNA.getSessionData(String key)`                      | Retrieve a session value by key.                                                                                                                                                                                                 |
| `clearSessionData` | `Future<void> AppDNA.clearSessionData()`                                 | Clear all session data.                                                                                                                                                                                                          |
| `getLocationData`  | `Future<LocationData?> AppDNA.deepLinks.getLocationData(String fieldId)` | Resolve the location the user picked for an onboarding `location` field, by field ID. Returns `null` when the field has no captured location. (Lives on the `deepLinks` module — there is no `AppDNA.getLocationData()` static.) |

## Consent + Traits

| Method             | Signature                                             | Description                                                       |
| ------------------ | ----------------------------------------------------- | ----------------------------------------------------------------- |
| `isConsentGranted` | `Future<bool> AppDNA.isConsentGranted()`              | Check whether analytics consent is currently granted.             |
| `getUserTraits`    | `Future<Map<String, dynamic>> AppDNA.getUserTraits()` | Get the traits map currently associated with the identified user. |

## Experiment Methods

| Method                 | Signature                                                                     | Description                                            |
| ---------------------- | ----------------------------------------------------------------------------- | ------------------------------------------------------ |
| `getExperimentVariant` | `Future<String?> AppDNA.getExperimentVariant(String experimentId)`            | Get the assigned variant for an experiment.            |
| `isInVariant`          | `Future<bool> AppDNA.isInVariant(String experimentId, String variantId)`      | Check whether the user is in a specific variant.       |
| `getExperimentConfig`  | `Future<dynamic> AppDNA.getExperimentConfig(String experimentId, String key)` | Get a specific config value for an experiment variant. |

## Push Methods (Static)

These static helpers mirror `AppDNA.push.*` — use either style.

| Method               | Signature                                                              | Description                                                         |
| -------------------- | ---------------------------------------------------------------------- | ------------------------------------------------------------------- |
| `setPushToken`       | `Future<void> AppDNA.setPushToken(String token)`                       | Register the APNs (iOS) or FCM (Android) device token.              |
| `setPushPermission`  | `Future<void> AppDNA.setPushPermission(bool granted)`                  | Report the current push permission status.                          |
| `trackPushDelivered` | `Future<void> AppDNA.trackPushDelivered(String pushId)`                | Track that a push notification was delivered.                       |
| `trackPushTapped`    | `Future<void> AppDNA.trackPushTapped(String pushId, {String? action})` | Track that a push notification was tapped, with optional action ID. |

## Presentation Methods

| Method              | Signature                                                                  | Description                                                                                  |
| ------------------- | -------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| `presentOnboarding` | `Future<void> AppDNA.presentOnboarding(String flowId)`                     | Present an onboarding flow by ID.                                                            |
| `presentPaywall`    | `Future<bool> AppDNA.presentPaywall(String id, {PaywallContext? context})` | Present a paywall by ID with optional context. Returns whether a paywall was actually shown. |

## Web Entitlements

| Member                    | Signature                                                | Description                                        |
| ------------------------- | -------------------------------------------------------- | -------------------------------------------------- |
| `webEntitlement`          | `Future<WebEntitlement?> AppDNA.webEntitlement`          | The current web entitlement, if any.               |
| `onWebEntitlementChanged` | `Stream<WebEntitlement?> AppDNA.onWebEntitlementChanged` | Broadcast stream emitting web entitlement changes. |

## Deep Links

| Method                  | Signature                                                  | Description                                                         |
| ----------------------- | ---------------------------------------------------------- | ------------------------------------------------------------------- |
| `checkDeferredDeepLink` | `Future<DeferredDeepLink?> AppDNA.checkDeferredDeepLink()` | Check for a deferred deep link captured during install attribution. |

***

## Module Namespaces

Access module-specific functionality through these static properties on `AppDNA`.

| Property               | Type                        | Description                                           |
| ---------------------- | --------------------------- | ----------------------------------------------------- |
| `AppDNA.push`          | `AppDNAPushModule`          | Push notification registration, tokens, and delegates |
| `AppDNA.billing`       | `AppDNABilling`             | Products, purchases, entitlements, and restore        |
| `AppDNA.onboarding`    | `AppDNAOnboardingModule`    | Server-driven onboarding flow presentation            |
| `AppDNA.paywall`       | `AppDNAPaywallModule`       | Server-driven paywall presentation                    |
| `AppDNA.remoteConfig`  | `AppDNARemoteConfigModule`  | Remote configuration access                           |
| `AppDNA.features`      | `AppDNAFeaturesModule`      | Feature flag evaluation                               |
| `AppDNA.experiments`   | `AppDNAExperimentsModule`   | Experiment variant assignment                         |
| `AppDNA.inAppMessages` | `AppDNAInAppMessagesModule` | In-app message display                                |
| `AppDNA.surveys`       | `AppDNASurveysModule`       | User survey presentation                              |
| `AppDNA.deepLinks`     | `AppDNADeepLinksModule`     | Deep link and deferred deep link handling             |
| `AppDNA.screen`        | `AppDNAScreenModule`        | Server-driven screen lifecycle delegate               |

***

## AppDNAPushModule

| Method              | Signature                                                   | Description                                                                |
| ------------------- | ----------------------------------------------------------- | -------------------------------------------------------------------------- |
| `setToken`          | `Future<void> setToken(String token)`                       | Register the device push token.                                            |
| `setPermission`     | `Future<void> setPermission(bool granted)`                  | Update push permission status.                                             |
| `trackDelivered`    | `Future<void> trackDelivered(String pushId)`                | Track push delivery.                                                       |
| `trackTapped`       | `Future<void> trackTapped(String pushId, {String? action})` | Track push tap with optional action ID.                                    |
| `requestPermission` | `Future<bool> requestPermission()`                          | Request push notification permission from the OS. Returns whether granted. |
| `getToken`          | `Future<String?> getToken()`                                | Get the current push token, if registered.                                 |
| `setDelegate`       | `void setDelegate(AppDNAPushDelegate? delegate)`            | Set a delegate to receive push lifecycle callbacks. Pass `null` to clear.  |

## AppDNABilling

| Method                          | Signature                                                                       | Description                                                                  |
| ------------------------------- | ------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| `purchase`                      | `Future<PurchaseResult> purchase(String productId, {String? offerToken})`       | Initiate a purchase. Pass `offerToken` for Android base-plan offers.         |
| `restorePurchases`              | `Future<List<Entitlement>> restorePurchases()`                                  | Restore previous purchases and return all active entitlements.               |
| `getProducts`                   | `Future<List<ProductInfo>> getProducts(List<String> productIds)`                | Fetch localized product info from the store.                                 |
| `hasActiveSubscription`         | `Future<bool> hasActiveSubscription()`                                          | Check whether the user has an active subscription.                           |
| `getEntitlements`               | `Future<List<Entitlement>> getEntitlements()`                                   | Get all current entitlements for the user.                                   |
| `onEntitlementsChanged`         | `Stream<List<Entitlement>> onEntitlementsChanged`                               | Broadcast stream emitting entitlement changes.                               |
| `onEntitlementsChangedCallback` | `void onEntitlementsChangedCallback(void Function(List<Entitlement>) callback)` | Register a callback for entitlement changes. Alternative to the stream.      |
| `setDelegate`                   | `void setDelegate(AppDNABillingDelegate? delegate)`                             | Set a delegate to receive billing lifecycle callbacks. Pass `null` to clear. |

## AppDNAOnboardingModule

| Method        | Signature                                                           | Description                                                                     |
| ------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| `present`     | `Future<void> present(String flowId, {OnboardingContext? context})` | Present an onboarding flow with optional context.                               |
| `setDelegate` | `void setDelegate(AppDNAOnboardingDelegate? delegate)`              | Set a delegate to receive onboarding lifecycle callbacks. Pass `null` to clear. |

## AppDNAPaywallModule

| Method        | Signature                                                    | Description                                                                                  |
| ------------- | ------------------------------------------------------------ | -------------------------------------------------------------------------------------------- |
| `present`     | `Future<bool> present(String id, {PaywallContext? context})` | Present a paywall by ID with optional context. Returns whether a paywall was actually shown. |
| `setDelegate` | `void setDelegate(AppDNAPaywallDelegate? delegate)`          | Set a delegate to receive paywall lifecycle callbacks. Pass `null` to clear.                 |

## AppDNARemoteConfigModule

| Method      | Signature                               | Description                                                   |
| ----------- | --------------------------------------- | ------------------------------------------------------------- |
| `get`       | `Future<dynamic> get(String key)`       | Get a remote config value by key.                             |
| `refresh`   | `Future<void> refresh()`                | Force an immediate refresh of remote configuration.           |
| `getAll`    | `Future<Map<String, dynamic>> getAll()` | Get all remote config values as a map.                        |
| `onChanged` | `void onChanged(Function callback)`     | Register a callback invoked when remote config values change. |

## AppDNAFeaturesModule

| Method       | Signature                                 | Description                                             |
| ------------ | ----------------------------------------- | ------------------------------------------------------- |
| `isEnabled`  | `Future<bool> isEnabled(String flag)`     | Check whether a feature flag is enabled.                |
| `getVariant` | `Future<dynamic> getVariant(String flag)` | Get the variant value for a multi-variate feature flag. |
| `onChanged`  | `void onChanged(Function callback)`       | Register a callback invoked when feature flags change.  |

## AppDNAExperimentsModule

| Method         | Signature                                                         | Description                                        |
| -------------- | ----------------------------------------------------------------- | -------------------------------------------------- |
| `getVariant`   | `Future<String?> getVariant(String experimentId)`                 | Get the assigned variant for an experiment.        |
| `isInVariant`  | `Future<bool> isInVariant(String experimentId, String variantId)` | Check whether the user is in a specific variant.   |
| `getExposures` | `Future<List<Map<String, dynamic>>> getExposures()`               | Get all experiment exposures for the current user. |

## AppDNAInAppMessagesModule

| Method            | Signature                                                | Description                                                                  |
| ----------------- | -------------------------------------------------------- | ---------------------------------------------------------------------------- |
| `suppressDisplay` | `Future<void> suppressDisplay(bool suppress)`            | Suppress or resume in-app message display globally.                          |
| `setDelegate`     | `void setDelegate(AppDNAInAppMessageDelegate? delegate)` | Set a delegate to receive message lifecycle callbacks. Pass `null` to clear. |

## AppDNASurveysModule

| Method        | Signature                                          | Description                                                                 |
| ------------- | -------------------------------------------------- | --------------------------------------------------------------------------- |
| `present`     | `Future<void> present(String surveyId)`            | Present a survey by ID.                                                     |
| `setDelegate` | `void setDelegate(AppDNASurveyDelegate? delegate)` | Set a delegate to receive survey lifecycle callbacks. Pass `null` to clear. |

## AppDNADeepLinksModule

| Method        | Signature                                            | Description                                                            |
| ------------- | ---------------------------------------------------- | ---------------------------------------------------------------------- |
| `handleURL`   | `Future<void> handleURL(String url)`                 | Hand off an incoming deep link URL to the SDK for parsing and routing. |
| `setDelegate` | `void setDelegate(AppDNADeepLinkDelegate? delegate)` | Set a delegate to receive deep link callbacks. Pass `null` to clear.   |

## AppDNAScreenModule

| Method                          | Signature                                                                 | Description                                                                                                                                                                          |
| ------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `show`                          | `Future<void> show(String screenId, { Map<String, dynamic>? context })`   | Present a server-driven screen by ID. The result (`dismissed` + `responses`) arrives via the delegate `onScreenDismissed` callback as a `Map<String, dynamic>`.                      |
| `showFlow`                      | `Future<void> showFlow(String flowId, { Map<String, dynamic>? context })` | Present a multi-screen flow. The result (`completed`, `screensViewed`, `lastScreenId`, `responses`) arrives via the delegate `onFlowCompleted` callback as a `Map<String, dynamic>`. |
| `dismiss`                       | `Future<void> dismiss()`                                                  | Dismiss the currently-presented screen.                                                                                                                                              |
| `enableNavigationInterception`  | `Future<void> enableNavigationInterception()`                             | Inject screens between app navigations.                                                                                                                                              |
| `disableNavigationInterception` | `Future<void> disableNavigationInterception()`                            | Stop intercepting navigation events.                                                                                                                                                 |
| `preview`                       | `Future<Map<String, dynamic>?> preview(Map<String, dynamic> json)`        | Render a screen from raw JSON for debugging. Returns a preview result map (or `null`).                                                                                               |
| `setDelegate`                   | `void setDelegate(AppDNAScreenDelegate? delegate)`                        | Set a delegate to receive server-driven screen lifecycle callbacks. Pass `null` to clear.                                                                                            |

***

## Properties

| Property                        | Type             | Description                                                                                                                                            |
| ------------------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `AppDNA.getSdkVersion()`        | `Future<String>` | Returns the native SDK version string (e.g., iOS `"1.0.69"` or Android `"1.0.41"`).                                                                    |
| `AppDNA.currentBundleVersion()` | `Future<int?>`   | Method (not a property) returning the active config bundle version reported on events, or `null` if unknown. **Android-only** — returns `null` on iOS. |

***

## Delegate Abstract Classes

Implement these abstract classes and register them with the corresponding module's `setDelegate(...)`. Every `setDelegate` accepts a nullable delegate — pass `null` to clear.

### AppDNAOnboardingDelegate

```dart theme={null}
abstract class AppDNAOnboardingDelegate {
  void onOnboardingStarted(String flowId);
  void onOnboardingStepChanged(String flowId, String stepId, int stepIndex, int totalSteps);
  void onOnboardingCompleted(String flowId, Map<String, dynamic> responses);
  void onOnboardingDismissed(String flowId, int atStep);

  // Async hooks — see Onboarding > Async Step Hooks.
  // These return plain Maps (there are no StepAdvanceResult / StepConfigOverride classes);
  // an empty map / null means "proceed with defaults".
  Future<Map<String, dynamic>> onBeforeStepAdvance(
    String flowId,
    String fromStepId,
    int stepIndex,
    String stepType,
    Map<String, dynamic> responses,
    Map<String, dynamic>? stepData,
  ) async => <String, dynamic>{};

  Future<Map<String, dynamic>?> onBeforeStepRender(
    String flowId,
    String stepId,
    int stepIndex,
    String stepType,
    Map<String, dynamic> responses,
  ) async => null;

  Future<Map<String, dynamic>?> onElementInteraction(
    String flowId,
    String stepId,
    String blockId,
    String action,
    String? value,
    Map<String, dynamic> inputValues,
  ) async => null;

  Future<Map<String, dynamic>?> onPermissionRequest(String permissionType) async => null;

  void onPermissionResult(String flowId, String stepId, String permissionType, bool granted) {}
}
```

### AppDNAPaywallDelegate

```dart theme={null}
abstract class AppDNAPaywallDelegate {
  void onPaywallPresented(String paywallId);
  void onPaywallAction(String paywallId, String action);
  void onPaywallPurchaseStarted(String paywallId, String productId);
  void onPaywallPurchaseCompleted(String paywallId, String productId, Map<String, dynamic> transaction);
  void onPaywallPurchaseFailed(String paywallId, Object error, String errorType, String? productId);
  void onPaywallRestoreStarted(String paywallId);
  void onPaywallRestoreCompleted(String paywallId, List<String> restoredProductIds);
  void onPaywallRestoreFailed(String paywallId, Object error);
  void onPaywallDismissed(String paywallId);

  Future<bool> onPromoCodeSubmit(String paywallId, String code) async => false;
  void onPostPurchaseDeepLink(String paywallId, String url) {}
  void onPostPurchaseNextStep(String paywallId) {}
}
```

### AppDNAPushDelegate

```dart theme={null}
abstract class AppDNAPushDelegate {
  void onPushTokenRegistered(String token);
  void onPushReceived(Map<String, dynamic> notification, bool inForeground);
  void onPushTapped(Map<String, dynamic> notification, String? actionId);
}
```

### AppDNABillingDelegate

```dart theme={null}
abstract class AppDNABillingDelegate {
  void onPurchaseCompleted(String productId, Map<String, dynamic> transaction);
  void onPurchaseFailed(String productId, Object error);
  void onEntitlementsChanged(List<Map<String, dynamic>> entitlements);
  void onRestoreCompleted(List<String> restoredProductIds);
  void onBillingUnavailable() {} // Android-only; never fires on iOS.
}
```

### AppDNAInAppMessageDelegate

```dart theme={null}
abstract class AppDNAInAppMessageDelegate {
  void onMessageShown(String messageId, String trigger);
  void onMessageAction(String messageId, String action, Map<String, dynamic>? data);
  void onMessageDismissed(String messageId);
  bool shouldShowMessage(String messageId) => true;
}
```

`shouldShowMessage` is a veto — return `false` to suppress display before view construction and analytics.

### AppDNASurveyDelegate

```dart theme={null}
abstract class AppDNASurveyDelegate {
  void onSurveyPresented(String surveyId);
  void onSurveyCompleted(String surveyId, List<Map<String, dynamic>> responses);
  void onSurveyDismissed(String surveyId);
}
```

### AppDNADeepLinkDelegate

```dart theme={null}
abstract class AppDNADeepLinkDelegate {
  bool shouldOpen(String url, Map<String, dynamic> params);
  void onDeepLinkReceived(String url, Map<String, dynamic> params);
}
```

`shouldOpen` is a veto — return `false` to suppress default deep-link handling (e.g., defer until the user has signed in).

### AppDNAScreenDelegate

```dart theme={null}
abstract class AppDNAScreenDelegate {
  void onScreenPresented(String screenId);
  void onScreenDismissed(String screenId, Map<String, dynamic> result);
  void onFlowCompleted(String flowId, Map<String, dynamic> result);
  bool onScreenAction(String screenId, Map<String, dynamic> action);
}
```

`onScreenAction` is a veto — return `false` to intercept the action and prevent default handling.

***

## Key Types

### AppDNAEnvironment

```dart theme={null}
enum AppDNAEnvironment { production, staging }
```

### AppDNAOptions

```dart theme={null}
class AppDNAOptions {
  final int? flushInterval;            // Default: 30 (seconds)
  final int? batchSize;                // Default: 20
  final int? configTTL;                // Default: 3600 (seconds)
  final AppDNALogLevel? logLevel;      // Default: warning
  final AppDNABillingProvider? billingProvider; // Default: storeKit2 (Google Play Billing on Android)
  final int? notificationIcon;         // Android only: notification small-icon drawable resource id
  @Deprecated('Ignored since 1.0.8 — the native bridge injects the framework tag itself.')
  final String? framework;             // Ignored; the bridge injects `flutter` unconditionally
  final bool? requireConsent;          // Default: false (analytics opt-out)
  final int? vetoTimeout;              // Default: 5 (seconds a host veto may take)

  const AppDNAOptions({
    this.flushInterval,
    this.batchSize,
    this.configTTL,
    this.logLevel,
    this.billingProvider,
    this.notificationIcon,
    this.framework,
    this.requireConsent,
    this.vetoTimeout,
  });
}
```

### AppDNALogLevel

```dart theme={null}
enum AppDNALogLevel { none, error, warning, info, debug }
```

### AppDNABillingProvider

`AppDNABillingProvider` is a class (not an enum): value-less providers are static consts, while `adapty` is a factory carrying the Adapty public SDK key.

```dart theme={null}
class AppDNABillingProvider {
  static const AppDNABillingProvider storeKit2 = ...;
  static const AppDNABillingProvider revenueCat = ...;
  static const AppDNABillingProvider none = ...;

  /// Adapty billing, keyed by your Adapty public SDK key.
  factory AppDNABillingProvider.adapty(String apiKey);
}
```

### onBeforeStepAdvance result (a Map)

`onBeforeStepAdvance` returns a plain `Map<String, dynamic>` — there is no `StepAdvanceResult` Dart class. An empty map (or `{'type': 'proceed'}`) means "advance normally". The conceptual shapes:

```dart theme={null}
// Advance normally (the default):
<String, dynamic>{}                                    // or {'type': 'proceed'}

// Advance and merge extra data into the flow responses:
{'type': 'proceedWithData', 'data': {...}}

// Block the advance and show an error message:
{'type': 'block', 'message': 'Invalid email or password.'}

// Jump to a specific step (optionally merging data):
{'type': 'skipTo', 'stepId': 'plan_step', 'data': {...}}
```

### onBeforeStepRender result (a Map)

`onBeforeStepRender` returns a `Map<String, dynamic>?` — there is no `StepConfigOverride` Dart class. Return `null` for no override, or a map of the fields to override:

```dart theme={null}
// null == no override, or a config-override map, e.g.:
{
  'fieldDefaults': {...},   // pre-fill form fields, keyed by Console field ID
  'title': 'Welcome back',  // override the step title
  'subtitle': '...',        // override the step subtitle
  'ctaText': 'Continue',    // override the primary CTA label
  'layoutOverrides': {...}, // override layout-level config (alignment, spacing, theme tokens)
}
```

### PaywallAction values

There is no `PaywallAction` Dart class. The `action` argument passed to `AppDNAPaywallDelegate.onPaywallAction(paywallId, action)` is a plain `String`, one of:

| Value              | Description                                     |
| ------------------ | ----------------------------------------------- |
| `cta_tapped`       | The main call-to-action button was tapped       |
| `feature_selected` | A feature item was selected                     |
| `plan_changed`     | The user switched between plan options          |
| `link_tapped`      | A link (e.g., terms, privacy policy) was tapped |
| `custom`           | A custom action defined in the paywall config   |

### DismissReason values

There is no `DismissReason` Dart class. The native SDK reports a dismiss reason in analytics events; on Flutter you receive the lifecycle via `onPaywallDismissed`. The reported string values are:

| Value             | Description                                                                      |
| ----------------- | -------------------------------------------------------------------------------- |
| `purchased`       | Dismissed after a successful purchase                                            |
| `restore_success` | Dismissed after a restore returned at least one product (routes to success path) |
| `dismissed`       | Dismissed by the user via a close button                                         |
| `tappedOutside`   | Dismissed by tapping outside the paywall                                         |
| `programmatic`    | Dismissed programmatically by your code                                          |

### Screen dismissal result (a Map)

`AppDNA.screen.show(...)` returns `Future<void>`. The result is delivered to your `AppDNAScreenDelegate.onScreenDismissed(screenId, result)` as a `Map<String, dynamic>` with this shape:

```dart theme={null}
// result map:
{
  'dismissed': bool,
  'responses': <String, dynamic>{...},
}
```

### Flow completion result (a Map)

`AppDNA.screen.showFlow(...)` returns `Future<void>`. The result is delivered to your `AppDNAScreenDelegate.onFlowCompleted(flowId, result)` as a `Map<String, dynamic>` with this shape:

```dart theme={null}
// result map:
{
  'completed': bool,
  'screensViewed': <String>[...], // ordered list of screen IDs viewed during the flow
  'lastScreenId': String?,
  'responses': <String, dynamic>{...},
}
```

### LocationData

Result of `AppDNA.deepLinks.getLocationData(String fieldId)` — the location a user picked for an onboarding `location` field:

```dart theme={null}
class LocationData {
  final String formattedAddress;
  final String city;
  final String state;
  final String stateCode;
  final String country;
  final String countryCode;
  final double latitude;
  final double longitude;
  final String timezone;
  final int timezoneOffset;
  final String? postalCode;
  final String rawQuery;

  const LocationData({
    required this.formattedAddress,
    required this.city,
    required this.state,
    required this.stateCode,
    required this.country,
    required this.countryCode,
    required this.latitude,
    required this.longitude,
    required this.timezone,
    required this.timezoneOffset,
    this.postalCode,
    required this.rawQuery,
  });
}
```

### PaywallContext

```dart theme={null}
class PaywallContext {
  final String? placement;
  final String? experiment;
  final String? variant;
  final Map<String, dynamic>? customData;

  const PaywallContext({
    this.placement,
    this.experiment,
    this.variant,
    this.customData,
  });
}
```

### OnboardingContext

```dart theme={null}
class OnboardingContext {
  final String? source;
  final String? campaign;
  final String? referrer;
  final Map<String, dynamic>? userProperties;
  final Map<String, String>? experimentOverrides;

  const OnboardingContext({
    this.source,
    this.campaign,
    this.referrer,
    this.userProperties,
    this.experimentOverrides,
  });
}
```

### Push notification payload (a Map)

There is no public `PushPayload` Dart class. The `notification` argument delivered to the `AppDNAPushDelegate` callbacks (`onPushReceived` / `onPushTapped`) is a raw `Map<String, dynamic>` with camelCase keys:

| Key        | Type                    | Description                                                                       |
| ---------- | ----------------------- | --------------------------------------------------------------------------------- |
| `pushId`   | `String`                | Unique identifier for the notification                                            |
| `title`    | `String`                | Notification title                                                                |
| `body`     | `String`                | Notification body text                                                            |
| `imageUrl` | `String?`               | URL to a rich notification image                                                  |
| `data`     | `Map<String, dynamic>?` | Custom data payload                                                               |
| `action`   | `Map<String, dynamic>?` | Action as `{type, value}` (e.g. `{"type": "deep_link", "value": "app://screen"}`) |

### Entitlement

```dart theme={null}
class Entitlement {
  final String productId;
  final String store;          // e.g. "app_store", "play_store"
  final String status;         // e.g. "active", "expired"
  final String? expiresAt;     // ISO 8601 timestamp
  final bool isTrial;
  final String? offerType;     // e.g. "introductory"
}
```

### PurchaseResult

```dart theme={null}
class PurchaseResult {
  final String status;            // "purchased", "cancelled", "pending", "unknown"
  final Entitlement? entitlement; // Non-null when status == "purchased"
}
```

### ProductInfo

```dart theme={null}
class ProductInfo {
  final String id;
  final String name;
  final String description;
  final String displayPrice;   // Localized formatted price
  final double price;          // Numeric price value
  final String? offerToken;    // Android base-plan offer token
}
```

### WebEntitlement

```dart theme={null}
class WebEntitlement {
  final bool isActive;
  final String? planName;
  final String? priceId;
  final String? interval;          // e.g. "month", "year"
  final String status;             // e.g. "active", "canceled", "trialing"
  final DateTime? currentPeriodEnd;
  final DateTime? trialEnd;
}
```

### DeferredDeepLink

```dart theme={null}
class DeferredDeepLink {
  final String screen;
  final Map<String, String> params;
  final String visitorId;
}
```

### SurveyResult

```dart theme={null}
class SurveyResult {
  final String surveyId;
  final bool completed;            // false if dismissed early
  final int questionsAnswered;
  final List<SurveyAnswer>? answers;
}
```

### SurveyAnswer

```dart theme={null}
class SurveyAnswer {
  final String questionId;
  final dynamic answer;            // int, String, List, etc. depending on question type
}
```

***

## Notes

### Architecture

The Flutter package exposes API facades, DTOs, and platform-channel bridges; rendering, business logic, network I/O, and storage execute in the native iOS (Swift) and Android (Kotlin) SDKs. New native behavior reaches Flutter via mechanical codegen — the Dart API surface stays continuously in sync with the native ones.

### Versioning

The Dart API surface stays continuously in sync with the bundled native renderer through mechanical codegen. CI enforces that the published Flutter package, the iOS SDK, and the Android SDK all advance together — every Flutter release ships with the matching renderer baked in. See the supported-versions box at the top of this page for the active versions.

### Platform-specific behavior

* `AppDNABillingProvider.storeKit2` and `AppDNABillingProvider.revenueCat` apply on iOS only. On Android, billing always uses Google Play Billing.
* `requestPermission()` on `AppDNAPushModule` triggers the iOS APNs permission prompt or the Android 13+ `POST_NOTIFICATIONS` runtime permission.
* `shutdown()` releases native resources on Android. On iOS it is a no-op.
* `offerToken` on `purchase(...)` is required for Android subscription offers (base-plan tokens) and ignored on iOS.
