The AppDNA Flutter SDK is designed to work reliably in offline and poor network conditions. All offline logic is delegated to the native iOS and Android SDKs, which handle config caching, event queuing, and bundled fallback configs transparently.
Config Resolution Priority
When the SDK needs configuration data, it follows this priority order:
| Priority | Source | Description |
|---|
| 1 | Remote | Fresh config fetched from the AppDNA backend |
| 2 | Cached | Previously fetched config stored on device |
| 3 | Bundled | Static config file shipped with the app binary |
The SDK always attempts to fetch the latest remote config. If the network is unavailable, it falls back to the cached config. If no cache exists (e.g., on first launch with no connectivity), it uses the bundled config.
Config TTL
The configTTL option controls how long cached config is considered fresh before the SDK attempts to fetch a new version from the server:
await AppDNA.configure(
"adn_live_xxx",
environment: AppDNAEnvironment.production,
options: AppDNAOptions(
configTTL: 300, // 5 minutes (default)
),
);
| Parameter | Type | Default | Description |
|---|
configTTL | int? | 300 | Seconds before cached config is considered stale |
When the TTL expires, the SDK will attempt to fetch fresh config on the next access. If the fetch fails, the existing cached config continues to be used.
Event Queue
Events tracked while the device is offline are stored in a persistent queue on the device. The queue is flushed automatically when connectivity is restored.
Queue Configuration
Control the flush behavior through AppDNAOptions:
await AppDNA.configure(
"adn_live_xxx",
environment: AppDNAEnvironment.production,
options: AppDNAOptions(
flushInterval: 30, // Seconds between automatic flushes (default: 30)
batchSize: 20, // Events per batch (default: 20)
),
);
| Parameter | Type | Default | Description |
|---|
flushInterval | int? | 30 | Seconds between automatic event flushes |
batchSize | int? | 20 | Number of events to batch before flushing |
How the Queue Works
- Events are persisted to local storage immediately when
track() is called.
- The SDK attempts to flush events either when the
batchSize is reached or every flushInterval seconds.
- If a flush fails due to network issues, events remain in the queue and are retried on the next flush cycle.
- When connectivity is restored, all queued events are sent in order.
Events are never dropped due to network failures. The queue persists across app restarts, ensuring no data is lost even if the app is terminated while offline.
Bundled Config
To ensure the SDK has a valid configuration on first launch — even without network connectivity — you can bundle a static config file with your app.
iOS
Add the config file to your Xcode project bundle:
- Download the config bundle from the AppDNA Console under Settings > SDK > Config Bundle.
- Save it as
appdna-config.json.
- Add it to your Xcode project by dragging it into the project navigator.
- Ensure the file is included in your target’s Build Phases > Copy Bundle Resources.
Android
Place the config file in the Android assets directory:
android/app/src/main/assets/appdna-config.json
- Download the config bundle from the AppDNA Console under Settings > SDK > Config Bundle.
- Save it as
appdna-config.json.
- Place it at
android/app/src/main/assets/appdna-config.json.
The bundled config is a snapshot. It will be used only when no remote or cached config is available. To keep the bundled config up to date, regenerate and replace it as part of your CI/CD pipeline before each release.
Config Bundle Version
The bundle version is managed by the native layer. When the SDK resolves config, it compares the cached config version with the remote version to determine whether an update is needed. The bundled config is treated as version 0 and is always superseded by any cached or remote config.
EventChannel Streams
The SDK uses Flutter EventChannels to stream real-time data from the native layer. These streams work across the full app lifecycle, including when the app transitions between foreground and background states:
| EventChannel | Purpose |
|---|
com.appdna.sdk/web_entitlement | Web entitlement change stream |
com.appdna.sdk/push_received | Push notification received stream |
com.appdna.sdk/push_tapped | Push notification tapped stream |
com.appdna.sdk/entitlements | Entitlement changes stream |
EventChannel streams automatically reconnect when the app returns from the background. You do not need to re-subscribe to streams after lifecycle transitions.
Offline Behavior Summary
| Feature | Offline Behavior |
|---|
| Event tracking | Events queued locally, flushed when connectivity restored |
| Remote config | Falls back to cached config, then bundled config |
| Feature flags | Uses cached/bundled values until remote config is refreshed |
| Experiments | Variant assignments are cached; new assignments wait for network |
| Push registration | Token registration is retried when connectivity restored |
| Purchases | Handled by the native store SDK (StoreKit / Google Play Billing) |
Best Practices
- Always bundle a config file for first-launch reliability.
- Keep
configTTL reasonable — 5 minutes (default) balances freshness with network efficiency.
- Call
flush() before background — While the SDK handles this automatically, explicitly flushing before the app enters the background provides an extra safety net.
- Do not assume network availability — Design your app UI to work with cached config values. Use
onReady() to know when remote config has been loaded.
// Flush events before background
void onAppLifecycleStateChanged(AppLifecycleState state) {
if (state == AppLifecycleState.paused) {
AppDNA.flush();
}
}