Platforms
Flutter
Add the Flutter plugin and get deep links as a Dart stream.
One Dart API for Android and iOS. The plugin wraps the native Android and iOS SDKs, which do the work. It needs Flutter 3.38 or newer, Android 5.0 (API 21) or newer, and iOS 13 or newer.
Add the package
Terminal flutter pub add dropplink_flutter cd ios && pod installPass your SDK keys at build time
Each platform has its own key in Settings → SDK keys. Pass them with
--dart-defineso they're never in your Dart source:Terminal flutter run \ --dart-define=DROPPLINK_ANDROID_KEY_ID=ak_3f9c1e2a7b6d4c80 \ --dart-define=DROPPLINK_ANDROID_SECRET=your_android_secret \ --dart-define=DROPPLINK_IOS_KEY_ID=ak_9b2d4f6a8c0e1357 \ --dart-define=DROPPLINK_IOS_SECRET=your_ios_secretTo avoid typing them every time, put them in a git-ignored
dropplink.jsonand pass--dart-define-from-file=dropplink.json. In CI, write the file from your secrets.Initialize in main()
lib/main.dart import 'dart:io'; import 'package:dropplink_flutter/dropplink_flutter.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; Future<void> main() async { WidgetsFlutterBinding.ensureInitialized(); await Dropplink.initialize( keyId: Platform.isIOS ? const String.fromEnvironment('DROPPLINK_IOS_KEY_ID') : const String.fromEnvironment('DROPPLINK_ANDROID_KEY_ID'), secret: Platform.isIOS ? const String.fromEnvironment('DROPPLINK_IOS_SECRET') : const String.fromEnvironment('DROPPLINK_ANDROID_SECRET'), debug: kDebugMode, ); runApp(const MyApp()); }initializereturns as soon as the SDK starts. The network call runs in the background.Listen for deep links
Subscribe where you can navigate, for example in your root widget's
initState:lib/app.dart late final StreamSubscription<DropplinkDeepLink> _links; @override void initState() { super.initState(); _links = Dropplink.onDeepLink.listen((link) { // link.path = "/product/123", link.params = {"variant": "red"} router.go(link.path, extra: link.params); }); } @override void dispose() { _links.cancel(); super.dispose(); }If a link arrives before you subscribe, which is common for the deferred link, it's delivered as soon as you do.
Set up App Links and Universal Links
Finish the deep link setup: the
autoVerifyintent filter inandroid/app/src/main/AndroidManifest.xml, and the Associated Domains capability in Xcode. The plugin forwards the links your app is opened with to the SDK by itself, so there's no native code to write.Flutter's own deep linking would also try to open the link's code, like
/diwali, as a route. Turn it off so links reach your app only throughonDeepLink:android/app/src/main/AndroidManifest.xml (inside <activity>) <meta-data android:name="flutter_deeplinking_enabled" android:value="false" />ios/Runner/Info.plist <key>FlutterDeepLinkingEnabled</key> <false/>
That's the whole integration. Next, test an install before you release.
Already handling links with app_links?#
If your app already receives links through a package like app_links, turn off the plugin's automatic forwarding and pass the
links yourself, so each open is reported once:
<meta-data android:name="app.ondropp.dropplink.auto_handle_links" android:value="false" /><key>DropplinkAutoHandleLinks</key>
<false/>appLinks.uriLinkStream.listen((uri) => Dropplink.handleDeepLink(uri.toString()));Events and users#
Dropplink.track('add_to_cart', props: {'sku': 'TSHIRT-M', 'qty': 1});
Dropplink.track('purchase', revenue: Revenue(amount: 499, currency: 'INR', orderId: 'A1023'));
await Dropplink.identify(user.id, email: user.email, phone: user.phone); // after sign-up or login
await Dropplink.logout();
await Dropplink.setTrackingEnabled(false); // the user declined trackingTrack the funnel with track, and call identify with your own user ID as soon as someone signs up or logs in (and on every launch
after that), so their events and revenue follow them. Events are saved on the phone and sent in batches, so you don't need to handle
networking or being offline. Send purchases that should pay creators from your server: revenue tracked in the app is
shown as app-reported.
More: Tracking events · Users and revenue credit
API#
abstract final class Dropplink {
static Future<void> initialize({required String keyId, required String secret, bool debug = false});
static Stream<DropplinkDeepLink> get onDeepLink; // broadcast stream
static Future<void> handleDeepLink(String url);
static Future<DropplinkAttribution?> getAttribution(); // null before the first successful launch report
static Future<String?> getInstallId();
static Future<void> track(String name, {Map<String, Object?>? props, Revenue? revenue});
static Future<void> identify(String userId, {String? email, String? phone});
static Future<void> logout();
static Future<void> setTrackingEnabled(bool enabled);
static Future<bool> isTrackingEnabled();
}
class Revenue {
const Revenue({required double amount, required String currency, String? orderId});
}
class DropplinkDeepLink {
final String path; // "/product/123"
final Map<String, String> params;
final String? url; // the opened URL; null for a deferred link
final bool isDeferred; // true only for the link delivered after a fresh install
}
class DropplinkAttribution {
final String type; // "attributed" or "organic"
final String method; // "install_referrer", "fingerprint" or "none"
final String? clickId, linkId, campaignId, creatorId, attributedAt;
final String? source; // the channel, e.g. "instagram"
final double? confidence;
}initialize throws a PlatformException with code invalid_config if a key ID or secret is empty or malformed, which usually means
a --dart-define is missing. Network errors never reach Dart: the native SDK retries them.