Flutter SDK
Install and integrate the LinkTrail Flutter SDK — package linktrail_flutter, one Dart API over the native SDKs.
A thin plugin over the native LinkTrail SDKs · package linktrail_flutter on pub.dev · entry point LinkTrail · Android minSdk 26, iOS 15+. Wraps io.linktrail:sdk (Maven Central) and LinkTrailSDK (CocoaPods) behind one Dart API.
Before this works, set up three things in the dashboard. Skip one and links fail silently — they open the browser instead of your app. Full flow: Quickstart.
- 1Register both app targets — Apps → Register app. iOS needs your Apple Team ID and bundle ID; Android needs its package name and SHA-256 fingerprints.
- 2Add a link domain — Domains. Or use your free
*.linktrail.iosubdomain. - 3Create an API key — Settings → API Keys. That is the
lt_live_…value below.
Install
flutter pub add linktrail_flutterOr add it to your app's pubspec.yaml, then flutter pub get:
dependencies:
linktrail_flutter: ^0.0.3The native SDKs are pulled in automatically — no manual Gradle or CocoaPods edits. Platform minimums the SDK requires (set them if your app targets lower): android { defaultConfig { minSdk = 26 } } and platform :ios, '15.0'.
Integrate
import 'package:linktrail_flutter/linktrail_flutter.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
// One stream — fires for deferred (first-launch) AND re-engagement links:
LinkTrail.onLink.listen((event) {
router.route(event.link.path, event.link.customData);
});
LinkTrail.onError.listen((error) => debugPrint('LinkTrail: $error'));
// The API key is required. The install is tracked automatically.
await LinkTrail.configure(apiKey: 'lt_live_...');
runApp(const MyApp());
}Incoming links are captured automatically — you do not need to override MainActivity (Android) or AppDelegate/SceneDelegate (iOS). The plugin forwards App Links, Universal Links, and custom-scheme opens to the SDK on both cold start and while running. Every callback is a broadcast Stream, so you can listen from multiple places.
More
// Custom + revenue events:
await LinkTrail.trackEvent(name: 'purchase', value: 59.99, currency: 'USD');
// Cached results:
final attribution = await LinkTrail.lastAttribution;
final lastLink = await LinkTrail.lastDeepLink;
// Attribution stream (fires when an install is attributed):
LinkTrail.onAttribution.listen((attribution) { /* … */ });
// Consent-gated install (defer configure's auto-track, then call manually):
await LinkTrail.configure(apiKey: 'lt_live_...',
options: LinkTrailOptions(autoTrackInstall: false));
await LinkTrail.trackInstall();
// iOS ATT / SKAdNetwork (no-ops on Android):
await LinkTrail.requestTrackingAuthorization();
await LinkTrail.registerForSKAdAttribution();
await LinkTrail.updateConversionValue(3, coarseValue: LinkTrailCoarseConversionValue.medium);Errors arrive as typed LinkTrailException subtypes on onError, and are thrown from the Future-returning calls — so you can try/catch a specific case like LinkTrailInvalidApiKeyException.
Deferred attribution & the paste button (iOS)
On iOS, deferred attribution recovers a click token the tapped link leaves on the clipboard, and LinkTrailOptions.clickTokenSource chooses how it is read. The widget renders the native control on iOS 16+ and nothing on Android, where the Play Install Referrer handles deferred attribution instead.
| clickTokenSource | Behaviour | Trade-off |
|---|---|---|
| pasteButton (default) | Read only when the user taps a LinkTrailPasteButton. | No system alert, but it needs the widget and autoTrackInstall: false. |
| automatic | The SDK reads the clipboard itself at install. | No UI, but iOS shows the “Allow Paste” alert on first launch. |
With the default pasteButton you must render the widget and set autoTrackInstall: false, or deferred installs on iOS stay unattributed. Apple restricts customization of the control — no custom label text, font, or border.
LinkTrailPasteButton(
width: 240,
onToken: (token) async {
await LinkTrail.trackInstallWithClickToken(token);
},
)API surface
| API | Purpose |
|---|---|
| configure(apiKey:, options:) | Initialize; auto-tracks the install |
| onLink — Stream<LinkTrailLinkEvent> | Deep link delivered (deferred + re-engagement) |
| onAttribution — Stream<LinkTrailAttribution> | Install attribution result |
| onError — Stream<LinkTrailException> | Failures (invalid key, network…) |
| handleDeepLink(uri) | Resolve a link manually (links are also auto-captured) |
| trackEvent(name:, value:, currency:) | Custom + revenue events |
| trackInstall(force:) | Manual install track (with autoTrackInstall: false) |
| lastAttribution / lastDeepLink | Cached last results |
| requestTrackingAuthorization() | ATT prompt (iOS; no-op on Android) |
| registerForSKAdAttribution() / updateConversionValue(…) | SKAdNetwork (iOS; no-op on Android) |
Deep-link setup
The plugin captures links automatically, but the OS still needs to route the link to your app. Android: declare your App Links host in AndroidManifest.xml (see below). iOS: add the Associated Domains capability with applinks:yourapp.linktrail.io, and for a custom scheme add it under CFBundleURLTypes in ios/Runner/Info.plist.
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" android:host="yourapp.linktrail.io" />
</intent-filter>The apple-app-site-association / assetlinks.json files are generated and hosted by LinkTrail from the credentials you registered in the dashboard. On iOS the plugin registers on the UIScene lifecycle, so links are delivered on both the scene and classic app-delegate lifecycles.
Example app: KickFlip — a Flutter storefront in example/ wired end to end. cd example && flutter run --dart-define=LINKTRAIL_API_KEY=lt_live_...