Installation
Detour provides a Flutter plugin (detour_flutter_plugin) that bridges the native Detour SDKs on Android and iOS, so both builds share one Dart integration.
A Detour app in the Dashboard is required before the SDK can resolve links. See Getting Started.
Requirements
Section titled “Requirements”- Flutter:
>=3.3.0 - Dart:
^3.11.1 - Android min SDK:
24 - iOS:
13.0+
Links are processed on Android and iOS only. On web, macOS, Linux, and Windows the plugin loads a no-op so a multi-platform codebase keeps building. See Platform support.
Install package
Section titled “Install package”Add the package to your pubspec.yaml:
dependencies: detour_flutter_plugin: ^1.2.0Then run:
flutter pub getNative SDK dependencies
Section titled “Native SDK dependencies”detour_flutter_plugin wraps the native Detour SDKs:
- The Android bridge depends on
com.swmansion.detour:detour-sdk:1.1.0. - The iOS bridge depends on the
DetourCocoaPod (~> 1.1.0), pulled in automatically by the plugin podspec.
Keep google() and mavenCentral() in your project repositories so Gradle can resolve the Android dependency.
After changing iOS dependencies, run:
cd iospod installcd ..Credentials
Section titled “Credentials”Both values come from API configuration in the Dashboard: the appID and the publishable apiKey. See Getting Started.
Pass your credentials to DetourConfig:
const DetourConfig( apiKey: '<REPLACE_WITH_YOUR_API_KEY>', appID: '<REPLACE_WITH_APP_ID_FROM_PLATFORM>', shouldUseClipboard: true, linkProcessingMode: LinkProcessingMode.all,)Keep apiKey and appID in environment variables or another secure configuration layer.
Platform integration
Section titled “Platform integration”The plugin wires up the bridge and runtime listeners, but your app still needs native deep-link registration. The Dashboard generates the Associated Domains and App Links configuration for verified HTTPS links. It does not generate custom-scheme registration.
Android
Section titled “Android”Verified App Links
Section titled “Verified App Links”Paste the generated App Links filter into the MainActivity declaration in
android/app/src/main/AndroidManifest.xml:
<activity android:name=".MainActivity" android:exported="true" android:launchMode="singleTop"> <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" /> <data android:host="<your-host>" /> <data android:pathPrefix="/<app-hash>" /> <!-- The Dashboard adds any required legacy short-link prefixes. --> </intent-filter></activity>Use the exact host and paths generated in App configuration. New short links
reuse /<app-hash> and do not require another manifest entry or native build.
If your app is served from a custom domain, the Dashboard generates the filter for that domain. Assign the domain before you copy the values. Changing the host an app is assigned to later needs a new build. Do not add hosts it did not generate: on Android 11 and lower that breaks App Link verification for every host at once, see Verification.
Custom-scheme links (optional)
Section titled “Custom-scheme links (optional)”Add a separate filter to the same activity only if you use links such as
myapp://product/42:
<intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="myapp" /></intent-filter>Callback delivery
Section titled “Callback delivery”No Android callback override is required. The plugin registers itself as a
Flutter NewIntentListener, processes the activity’s initial intent, and
receives later intents from the Flutter embedding.
Universal Links
Section titled “Universal Links”In Xcode, open the Runner target, select Signing & Capabilities, add the
Associated Domains capability, and enter applinks:<your-host>. The
resulting Runner.entitlements entry is:
<key>com.apple.developer.associated-domains</key><array> <string>applinks:<your-host></string></array>With a custom domain, the Dashboard generates this entry for that domain too.
Custom-scheme links (optional)
Section titled “Custom-scheme links (optional)”If you use scheme links, register the scheme under the Runner target’s
Info → URL Types, or add it directly to ios/Runner/Info.plist:
<key>CFBundleURLTypes</key><array> <dict> <key>CFBundleURLSchemes</key> <array> <string>myapp</string> </array> </dict></array>Delegate forwarding
Section titled “Delegate forwarding”Flutter plugins, Detour included, receive lifecycle callbacks through the Flutter delegate chain. If you override an iOS deep-link callback, return the super result:
override func application( _ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { return super.application( application, continue: userActivity, restorationHandler: restorationHandler )}
override func application( _ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { return super.application(app, open: url, options: options)}If your project defines a custom SceneDelegate, subclass
FlutterSceneDelegate (recommended) or forward scene events through
FlutterSceneLifeCycleProvider. An app using Flutter’s default scene delegate
does not need a custom class or callback overrides.
If your FlutterSceneDelegate subclass overrides either link callback, call
super so Flutter can forward it to Detour:
override func scene(_ scene: UIScene, continue userActivity: NSUserActivity) { super.scene(scene, continue: userActivity)}
override func scene( _ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) { super.scene(scene, openURLContexts: URLContexts)}After changing either native target, rebuild the app. A Dart hot reload does not
apply entitlements, Info.plist, or manifest changes.
Reference implementations in the example app:
Platform support
Section titled “Platform support”The plugin processes links on Android and iOS. On every other platform, that is web, macOS, Linux, and Windows, it loads a no-op implementation, so apps that target several platforms build and run without conditional imports:
configure(...)does nothing and logsDetour: current platform is not supported, skipping initialization.resolveInitialLink()returns a result withprocessed: trueand no link, so your startup flow continues normally.processLink(...)returns a result withprocessed: false.linkStreamis an empty stream.logEvent(...)andlogRetention(...)do nothing.
Call the SDK unconditionally from shared code. The no-op keeps unsupported platforms safe.
Next steps
Section titled “Next steps”- Wire up the startup and runtime flow in Usage.
- Start from the runnable app in Examples.
- Check the result. See Testing & Troubleshooting.