> ## 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 Quickstart

> Configure, identify, and track in 5 minutes

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

This guide walks you through configuring the AppDNA SDK, identifying users, and tracking your first event in a Flutter application.

## 1. Configure the SDK

Initialize AppDNA as early as possible in your app lifecycle, typically in your `main()` function before `runApp()`:

```dart theme={null}
import 'package:firebase_core/firebase_core.dart';
import 'package:appdna_sdk/appdna_sdk.dart';

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

  await Firebase.initializeApp();

  await AppDNA.configure(
    apiKey: "adn_live_xxx",
    env: AppDNAEnvironment.production,
    options: AppDNAOptions(logLevel: AppDNALogLevel.debug),
  );

  runApp(const MyApp());
}
```

<Note>
  **If you don't have your own Firebase:** Keep `Firebase.initializeApp()` as shown above.

  **If you already use Firebase** in your Flutter app: Remove the `Firebase.initializeApp()` call and just call `AppDNA.configure(...)`. The SDK automatically initializes its own named Firebase instance from `GoogleService-Info-AppDNA.plist` (iOS) and `google-services-appdna.json` (Android). Your existing Firebase setup is not affected.
</Note>

<Warning>
  Call `AppDNA.configure(...)` exactly once before using any other SDK methods. Calling it multiple times will result in undefined behavior.
</Warning>

### Configuration Options

The `AppDNAOptions` class lets you customize SDK behavior:

| Parameter          | Type                     | Default                           | Description                                                             |
| ------------------ | ------------------------ | --------------------------------- | ----------------------------------------------------------------------- |
| `flushInterval`    | `int?`                   | `30`                              | Seconds between automatic event flushes                                 |
| `batchSize`        | `int?`                   | `20`                              | Number of events to batch before flushing                               |
| `configTTL`        | `int?`                   | `3600`                            | Seconds before cached config is considered stale                        |
| `logLevel`         | `AppDNALogLevel?`        | `AppDNALogLevel.warning`          | Verbosity of SDK console logs                                           |
| `billingProvider`  | `AppDNABillingProvider?` | `AppDNABillingProvider.storeKit2` | Billing integration to use                                              |
| `notificationIcon` | `int?`                   | `null`                            | Android only: notification small-icon drawable resource id              |
| `requireConsent`   | `bool?`                  | `false`                           | When `true`, analytics stay off until `setConsent(true)`                |
| `vetoTimeout`      | `int?`                   | `5`                               | Seconds a host veto may take before the SDK applies the hook default    |
| `framework`        | `String?`                | `null`                            | Deprecated/ignored — the native bridge injects the framework tag itself |

### Environment

The `AppDNAEnvironment` enum controls which backend environment the SDK targets:

| Value                          | Description                      |
| ------------------------------ | -------------------------------- |
| `AppDNAEnvironment.production` | Production API and configuration |
| `AppDNAEnvironment.staging`    | Staging API for testing          |

### Log Level

The `AppDNALogLevel` enum controls console log verbosity:

| Value                    | Description                  |
| ------------------------ | ---------------------------- |
| `AppDNALogLevel.none`    | No logging                   |
| `AppDNALogLevel.error`   | Errors only                  |
| `AppDNALogLevel.warning` | Errors and warnings          |
| `AppDNALogLevel.info`    | Errors, warnings, and info   |
| `AppDNALogLevel.debug`   | All messages including debug |

<Info>
  Use `AppDNALogLevel.debug` during development to see all SDK activity. Switch to `AppDNALogLevel.warning` or `AppDNALogLevel.none` for production builds.
</Info>

### Billing Provider

The `AppDNABillingProvider` class specifies which billing system to use. The value-less providers are static consts; `adapty` is a factory that carries your Adapty public SDK key:

| Value                                              | Description                                                      |
| -------------------------------------------------- | ---------------------------------------------------------------- |
| `AppDNABillingProvider.storeKit2`                  | Native StoreKit 2 on iOS, Play Billing on Android (default)      |
| `AppDNABillingProvider.revenueCat`                 | RevenueCat integration                                           |
| `AppDNABillingProvider.adapty('<public-sdk-key>')` | Adapty integration — factory carrying your Adapty public SDK key |
| `AppDNABillingProvider.none`                       | Disable the billing module                                       |

<Note>
  On Android the SDK uses Google Play Billing under the StoreKit2 alias. RevenueCat is supported across both platforms when the native bridges are configured.
</Note>

## 2. Wait for Ready State

The SDK fetches remote configuration asynchronously. Use `onReady` to know when the SDK is fully initialized:

```dart theme={null}
await AppDNA.onReady(() {
  print("SDK ready -- remote config loaded");
});
```

`onReady()` accepts a callback that runs once the remote configuration has been fetched and applied.

## 3. Identify Users

Once a user signs in, call `identify` to associate events with their user ID:

```dart theme={null}
await AppDNA.identify(
  "user-123",
  traits: {
    "plan": "premium",
    "signup_date": "2025-01-15",
  },
);
```

<Note>
  Traits are merged with any previously set traits. You do not need to pass all traits on every call -- only the ones that have changed.
</Note>

## 4. Track Events

Track user actions with `track`:

```dart theme={null}
await AppDNA.track(
  "workout_completed",
  properties: {
    "duration": 45,
    "type": "strength",
  },
);
```

Events are batched and flushed automatically based on your `flushInterval` and `batchSize` settings.

## 5. Flush Events Manually

Force an immediate flush of all queued events:

```dart theme={null}
await AppDNA.flush();
```

This is useful before the app enters the background or when you need to ensure events are sent immediately.

## 6. Set User Consent

Control whether the SDK collects and sends analytics data:

```dart theme={null}
await AppDNA.setConsent(analytics: true);
```

<Warning>
  When `analytics` is set to `false`, events are silently dropped and not queued. No data is sent to AppDNA servers until consent is granted.
</Warning>

## 7. Change Log Level at Runtime

Adjust the log level without reconfiguring the SDK:

```dart theme={null}
AppDNA.setLogLevel("debug");
```

Valid levels: `"none"`, `"error"`, `"warning"`, `"info"`, `"debug"`.

## 8. Remote Config and Feature Flags

Retrieve server-side configuration values:

```dart theme={null}
final welcomeMessage = await AppDNA.getRemoteConfig("welcome_message");
```

Check whether a feature flag is enabled:

```dart theme={null}
final darkModeEnabled = await AppDNA.isFeatureEnabled("dark_mode");
```

## 9. Experiments

Get the variant assigned to a user for an experiment:

```dart theme={null}
final variant = await AppDNA.getExperimentVariant("paywall_test");
```

Check if the user is in a specific variant:

```dart theme={null}
final isInVariantB = await AppDNA.isInVariant("paywall_test", "b");
```

Inspect all experiment exposures collected during the current session:

```dart theme={null}
final exposures = await AppDNA.experiments.getExposures();
for (final exposure in exposures) {
  print("${exposure['experimentId']} → ${exposure['variant']}");
}
```

## 10. Session Data

Store cross-module session data that can be used in template interpolation across onboarding flows, paywalls, and in-app messages:

```dart theme={null}
// Store session data
await AppDNA.setSessionData('selected_plan', 'premium');
await AppDNA.setSessionData('referral_code', 'FRIEND2026');

// Retrieve session data
final plan = await AppDNA.getSessionData('selected_plan'); // "premium"

// Clear all session data
await AppDNA.clearSessionData();
```

Session data values are accessible in Console-configured content using template syntax: `{{session.selected_plan}}`. See the [Rich Media](/sdks/flutter/rich-media) guide for the full template namespace (`{{user.*}}`, `{{session.*}}`, `{{device.*}}`, `{{remote_config.*}}`).

## 11. Entitlements

Check whether the user has any active subscription (StoreKit, Play Billing, RevenueCat, or web entitlement):

```dart theme={null}
final isSubscribed = await AppDNA.billing.hasActiveSubscription();
```

Subscribe to live entitlement updates so your UI reflects renewals, cancellations, and restored purchases without polling:

```dart theme={null}
AppDNA.billing.onEntitlementsChanged.listen((entitlements) {
  final active = entitlements.where((e) => e.status == "active").toList();
  print("Active entitlements: ${active.length}");
});
```

See the [Billing](/sdks/flutter/billing) guide for the full purchase and restore flow.

## 12. Web Entitlement

Retrieve the current web entitlement (subscriptions granted outside the app store, e.g. via Stripe):

```dart theme={null}
final ent = await AppDNA.webEntitlement;
```

Listen for web entitlement changes in real time:

```dart theme={null}
AppDNA.onWebEntitlementChanged.listen((ent) {
  print("Web entitlement changed: $ent");
});
```

## 13. Deferred Deep Links

Check for a deferred deep link that brought the user to your app on first launch:

```dart theme={null}
final link = await AppDNA.checkDeferredDeepLink();
if (link != null) {
  // Navigate to the linked content
}
```

## 14. Get SDK Version

Print the underlying native SDK version (useful for support tickets):

```dart theme={null}
final version = await AppDNA.getSdkVersion();
print("Native SDK version: $version");
```

## 15. Reset on Logout

When a user signs out, call `reset` to clear the user identity and flush any remaining events:

```dart theme={null}
await AppDNA.reset();
```

This clears the identified user, generates a new anonymous ID, and flushes queued events.

## 16. Shutdown

When the app is terminating, shut down the SDK to ensure all events are flushed and resources are released:

```dart theme={null}
await AppDNA.shutdown();
```

On Android this releases native resources. On iOS this is a no-op because the SDK shuts down automatically when the process exits.

<Check>
  You now have the SDK configured with user identification, event tracking, remote config, experiments, entitlements, and deep links working. Continue to the module-specific guides for [Push Notifications](/sdks/flutter/push), [Billing](/sdks/flutter/billing), [Onboarding](/sdks/flutter/onboarding), and [Paywalls](/sdks/flutter/paywall).
</Check>
