# Integrating the Darwinium SDK into a Flutter app
This guide covers integrating the Darwinium profiling SDK into a cross‑platform
Flutter application. Flutter has no first‑party Darwinium package, so you
bridge to the native Android and iOS SDKs through a Flutter
MethodChannel.
How it works: the native SDK collects device, network and behavioural
signals and returns a base64‑encoded profiling blob. You attach that blob to
your backend requests as aDwn-Profilingheader, where it is evaluated at the
edge or in a backend Event API call. The blob is typically 4–5 KB, so you may
need to raise any header‑length limits on your edge/proxy.
What you need
| Requirement | Detail |
|---|---|
| Darwinium API token | Portal → Preferences → SDK Access → Generate Token. Gives a username + token used for the Maven repository. |
| Android | minSdk 23+ (Android 6). Native SDK com.darwinium:android_sdk (this demo uses 2.4.0). |
| iOS | iOS 12.0+ (see note below — SwiftProtobuf transitively requires 15.1). Native SDK dwn_ios_sdk via Swift Package Manager. |
| Maven credentials | Set DWN_MAVEN_USER, DWN_MAVEN_PASSWORD, and DWN_MAVEN_URL as environment variables (preferred) or Gradle properties. Never commit credentials. |
1. The Flutter (Dart) layer
Create a thin wrapper around a MethodChannel. Both platforms register a handler
on the same channel name (darwinium_sdk).
lib/darwinium_sdk.dart:
import 'dart:async';
import 'package:flutter/services.dart';
class DarwiniumSdk {
static const MethodChannel _channel = MethodChannel('darwinium_sdk');
Future<void> start({Map<String, Object?> config = const {}}) async {
await _channel.invokeMethod<void>('start', {'config': config});
}
Future<String> collect() async {
final value = await _channel.invokeMethod<String>('collect');
return value ?? '';
}
}
Use it from a page — start() once the page is up, collect() on user action:
final DarwiniumSdk _sdk = DarwiniumSdk();
@override
void initState() {
super.initState();
// Start after the first frame so the native engine/handler is ready.
WidgetsBinding.instance.addPostFrameCallback((_) => _sdk.start());
}
Future<void> _onSubmit() async {
final blob = await _sdk.collect(); // base64 profiling blob
// Attach to your API call:
// headers: {'Dwn-Profiling': blob}
}
See lib/main.dart for the full demo screen.
2. Android integration
2.1 Add the Maven repository and credentials
In android/build.gradle.kts, read credentials from the environment (with a
Gradle‑property fallback) and register the Darwinium Maven repo for all projects:
val dwnMavenUrl = System.getenv("DWN_MAVEN_URL")
?: providers.gradleProperty("DWN_MAVEN_URL").orNull
?: "https://packages.darwinium.com/artifactory/dwn-maven/"
val dwnMavenUser = System.getenv("DWN_MAVEN_USER")
?: providers.gradleProperty("DWN_MAVEN_USER").orNull
val dwnMavenPassword = System.getenv("DWN_MAVEN_PASSWORD")
?: providers.gradleProperty("DWN_MAVEN_PASSWORD").orNull
allprojects {
repositories {
google()
mavenCentral()
maven {
url = uri(dwnMavenUrl)
credentials {
username = dwnMavenUser
password = dwnMavenPassword
}
}
}
}
Export the credentials in your shell / CI (keep these out of source control):
export DWN_MAVEN_USER=your_user
export DWN_MAVEN_PASSWORD=your_token
export DWN_MAVEN_URL=https://packages.darwinium.com/artifactory/dwn-maven/
2.2 Add the SDK dependency
In android/app/build.gradle.kts:
dependencies {
implementation("com.darwinium:android_sdk:2.4.0")
}
2.3 Bridge Flutter → Android
In android/app/src/main/kotlin/com/example/my_app/MainActivity.kt, instantiate
DwnProfilingSDK and wire the channel. Forward permission results so the SDK can
react to location/phone‑state grants.
package com.example.my_app
import android.util.Log
import com.darwinium.dwn_sdk.DwnProfilingSDK
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
class MainActivity : FlutterActivity() {
private lateinit var dwnSdk: DwnProfilingSDK
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
dwnSdk = DwnProfilingSDK(this)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
.setMethodCallHandler { call, result ->
when (call.method) {
"start" -> {
val config = HashMap<String, Any>()
call.argument<Map<String, Any?>>("config")?.forEach { (k, v) ->
if (v != null) config[k] = v
}
dwnSdk.start(config, this)
result.success(null)
}
"collect" -> {
val profilingData = dwnSdk.collect()
Log.i(LOG_TAG, "collect() result: $profilingData")
result.success(profilingData)
}
else -> result.notImplemented()
}
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray,
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
dwnSdk.processPermissions(requestCode, grantResults)
}
companion object {
private const val CHANNEL = "darwinium_sdk"
private const val LOG_TAG = "DarwiniumSDK"
}
}
3. iOS integration (Swift Package Manager)
3.1 Add the Swift package
Open ios/Runner.xcworkspace in Xcode:
- File → Add Package Dependencies…
- URL:
https://packages.darwinium.com/sdk/dwn_ios_sdk.git - Choose a version rule (e.g. Up to Next Major from
2.3.1). - Add the
dwn_ios_sdkproduct to theRunnertarget. If you skip this you
will get module‑import errors.
3.2 Bridge Flutter → iOS
In ios/Runner/AppDelegate.swift, register the channel from
didInitializeImplicitFlutterEngine (the modern implicit‑engine entry point):
import Flutter
import UIKit
import DwnSDK_Framework
@main
@objc class AppDelegate: FlutterAppDelegate, FlutterImplicitEngineDelegate {
private let dwnSdk = DwnProfilingSDK()
private static let channel = "darwinium_sdk"
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
func didInitializeImplicitFlutterEngine(_ engineBridge: FlutterImplicitEngineBridge) {
GeneratedPluginRegistrant.register(with: engineBridge.pluginRegistry)
guard let messenger = engineBridge.pluginRegistry
.registrar(forPlugin: AppDelegate.channel)?.messenger() else { return }
FlutterMethodChannel(name: AppDelegate.channel, binaryMessenger: messenger)
.setMethodCallHandler { [weak self] call, result in
guard let self else { return }
switch call.method {
case "start":
let config = (call.arguments as? [String: Any])?["config"] as? [String: Any] ?? [:]
self.dwnSdk.start(config)
result(nil)
case "collect":
result(self.dwnSdk.collect())
default:
result(FlutterMethodNotImplemented)
}
}
}
}
iOS deployment target: The framework's
.swiftinterfacedeclares
ios12.0, but it transitively imports SwiftProtobuf which requires iOS
15.1. If Swift needs to rebuild the module from its interface you may have to
raise the deployment target accordingly.
4. Build and run
flutter pub get
flutter run -d emulator-5554 # Android
flutter run -d <ios-simulator-udid> # iOS
5. Viewing SDK logs
Android:
adb logcat -s DarwiniumSDK
iOS simulator:
xcrun simctl spawn booted log stream --level info \
--predicate 'eventMessage contains "DarwiniumSDK"'
6. Linking requests into a journey
For mobile flows that use ephemeral auth tokens (JWT or similar), map a
primary_session_tie value across steps so multiple requests are linked into a
single journey. Define the extraction with a JSONPath rule in your journey
configuration.
7. Troubleshooting
| Symptom | Fix |
|---|---|
| iOS: "Unable to resolve module dependency" | Ensure the dwn_ios_sdk product is linked to the Runner target and import DwnSDK_Framework is present in AppDelegate.swift. |
| Android: Maven auth failure | Confirm DWN_MAVEN_USER / DWN_MAVEN_PASSWORD are set and the repo URL is reachable. |
| Credentials in version control | Keep tokens in environment variables, never in gradle.properties. |