This guide walks you through configuring the AppDNA SDK, identifying users, and tracking your first event in a Flutter application.
Initialize AppDNA as early as possible in your app lifecycle, typically in your main() function before runApp():
import 'package:appdna_sdk/appdna_sdk.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await AppDNA.configure(
"adn_live_xxx",
environment: AppDNAEnvironment.production,
options: AppDNAOptions(logLevel: AppDNALogLevel.debug),
);
runApp(const MyApp());
}
Call AppDNA.configure(...) exactly once before using any other SDK methods. Calling it multiple times will result in undefined behavior.
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? | 300 | Seconds before cached config is considered stale |
logLevel | AppDNALogLevel? | AppDNALogLevel.warning | Verbosity of SDK console logs |
billingProvider | AppDNABillingProvider? | AppDNABillingProvider.storeKit2 | Billing integration to use |
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 |
Use AppDNALogLevel.debug during development to see all SDK activity. Switch to AppDNALogLevel.warning or AppDNALogLevel.none for production builds.
Billing Provider
The AppDNABillingProvider enum specifies which billing system to use:
| Value | Description |
|---|
AppDNABillingProvider.storeKit2 | Native StoreKit 2 (default, iOS only) |
AppDNABillingProvider.revenueCat | RevenueCat integration |
AppDNABillingProvider.none | Disable billing module |
2. Wait for Ready State
The SDK fetches remote configuration asynchronously. Use onReady to know when the SDK is fully initialized:
await AppDNA.onReady();
print("SDK ready -- remote config loaded");
onReady() returns a Future<void> that completes 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:
await AppDNA.identify(
"user-123",
traits: {
"plan": "premium",
"signup_date": "2025-01-15",
},
);
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.
4. Track Events
Track user actions with track:
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:
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:
await AppDNA.setConsent(true);
When consent is set to false, events are silently dropped and not queued. No data is sent to AppDNA servers until consent is granted.
7. Change Log Level at Runtime
Adjust the log level without reconfiguring the SDK:
AppDNA.setLogLevel("debug");
8. Remote Config and Feature Flags
Retrieve server-side configuration values:
final welcomeMessage = await AppDNA.getRemoteConfig("welcome_message");
Check whether a feature flag is enabled:
final darkModeEnabled = await AppDNA.isFeatureEnabled("dark_mode");
9. Experiments
Get the variant assigned to a user for an experiment:
final variant = await AppDNA.getExperimentVariant("paywall_test");
Check if the user is in a specific variant:
final isInVariantB = await AppDNA.isInVariant("paywall_test", "b");
10. Web Entitlement
Retrieve the current web entitlement:
final ent = await AppDNA.webEntitlement;
Listen for web entitlement changes in real time:
AppDNA.onWebEntitlementChanged.listen((ent) {
print("Entitlement changed: $ent");
});
11. Deferred Deep Links
Check for a deferred deep link that brought the user to your app:
final link = await AppDNA.checkDeferredDeepLink();
if (link != null) {
// Navigate to the linked content
}
12. Reset on Logout
When a user signs out, call reset to clear the user identity and flush any remaining events:
This clears the identified user, generates a new anonymous ID, and flushes queued events.
13. Shutdown
When the app is terminating, shut down the SDK to ensure all events are flushed and resources are released: