Flutter Detour by Software Mansion

Flutter Detour

Flutter SDK for handling deferred deep links with native Detour SDKs on Android and iOS.

Documentation

Check out our documentation page for integration guides and API details:

Other Detour SDKs

Detour is also available for other app stacks:

Create account on platform

Create account and configure your links: https://godetour.dev/auth/signup

Installation

Package

Add the package to your pubspec.yaml:

dependencies:
  detour_flutter_plugin: ^1.0.1

Install dependencies:

flutter pub get

Native SDK dependencies

This plugin uses native Detour SDK implementations:

  • Android: com.swmansion.detour:detour-sdk:1.0.0
  • iOS: embedded in this plugin package (no extra pod dependency)

Android

Make sure your Android repositories can resolve com.swmansion.detour:detour-sdk (for example via google() and mavenCentral() in your project repositories block).

iOS

No additional setup is required for the native iOS SDK dependency.

Run pods:

cd ios
pod install
cd ..

Usage

DetourService is the recommended orchestration layer. It:

  • configures SDK once,
  • merges initial and runtime link handling into a single pending intent,
  • exposes readiness via isInitialLinkProcessed,
  • uses explicit consume semantics with consumePendingIntent(),
  • suppresses short-window duplicate emissions.
import 'package:detour_flutter_plugin/detour_flutter_plugin.dart';

final detour = DetourService();

@override
void initState() {
  super.initState();
  detour.addListener(_onDetourChanged);
  _startDetour();
}

Future<void> _startDetour() async {
  await detour.start(
    const DetourConfig(
      apiKey: '<REPLACE_WITH_YOUR_API_KEY>',
      appID: '<REPLACE_WITH_APP_ID_FROM_PLATFORM>',
      shouldUseClipboard: true,
      linkProcessingMode: LinkProcessingMode.all,
    ),
  );
}

void _onDetourChanged() {
  final intent = detour.pendingIntent;
  if (intent == null) return;

  // Route once, then mark as consumed.
  // context.go(intent.link.route);
  detour.consumePendingIntent();
}

@override
void dispose() {
  detour.removeListener(_onDetourChanged);
  detour.dispose();
  super.dispose();
}

Use linkProcessingMode to control which sources are handled by SDK:

Value Universal/App links Deferred links Custom scheme links
LinkProcessingMode.all (default)
LinkProcessingMode.webOnly
LinkProcessingMode.deferredOnly

Custom scheme links require:

  • linkProcessingMode: LinkProcessingMode.all
  • native registration on each platform

Android (AndroidManifest.xml):

<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="detour-flutter-example" />
</intent-filter>

iOS (Info.plist):

<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>detour-flutter-example</string>
    </array>
  </dict>
</array>

Test commands:

# Android
adb shell am start -a android.intent.action.VIEW \
  -d "detour-flutter-example://products/42?source=scheme" \
  <your.package.name>

# iOS Simulator
xcrun simctl openurl booted "detour-flutter-example://products/42?source=scheme"

Low-level API

If you need full manual control, use DetourFlutterPlugin directly:

final plugin = DetourFlutterPlugin();

await plugin.configure(
  const DetourConfig(
    apiKey: '<REPLACE_WITH_YOUR_API_KEY>',
    appID: '<REPLACE_WITH_APP_ID_FROM_PLATFORM>',
  ),
);

final initial = await plugin.resolveInitialLink();
final stream = plugin.linkStream;
final processed = await plugin.processLink('https://example.com/path');

Analytics

Flutter API follows native SDK analytics contract:

  • predefined events via DetourEventName,
  • retention events as string names.
await detour.logEvent(
  DetourEventName.purchase,
  data: {'value': 9.99, 'currency': 'USD'},
);

await detour.logRetention('home_screen_viewed');

Types

DetourConfig

class DetourConfig {
  final String apiKey;
  final String appID;
  final bool shouldUseClipboard;
  final LinkProcessingMode linkProcessingMode;
}

DetourIntent

class DetourIntent {
  final DetourLink link;
  final DetourIntentSource source;
  final DateTime receivedAt;
}

DetourResult

class DetourResult {
  final bool processed;
  final DetourLink? link;
}
class DetourLink {
  final String url;
  final String route;
  final String pathname;
  final Map<String, String> params;
  final LinkType type;
}

DetourEventName

enum DetourEventName {
  login,
  search,
  share,
  signUp,
  tutorialBegin,
  tutorialComplete,
  reEngage,
  invite,
  openedFromPushNotification,
  addPaymentInfo,
  addShippingInfo,
  addToCart,
  removeFromCart,
  refund,
  viewItem,
  beginCheckout,
  purchase,
  adImpression,
}

API Reference

DetourService

High-level integration helper:

  • Future<void> start(DetourConfig config)
  • DetourIntent? get pendingIntent
  • void consumePendingIntent()
  • bool get isInitialLinkProcessed
  • Future<DetourResult> processLink(String url, {bool emitIntent = true})
  • Future<void> logEvent(DetourEventName eventName, {Map<String, dynamic>? data})
  • Future<void> logRetention(String eventName)
  • Future<void> stop()

DetourFlutterPlugin

Low-level bridge API:

  • Future<void> configure(DetourConfig config)
  • Future<DetourResult> resolveInitialLink()
  • Stream<DetourResult> get linkStream
  • Future<DetourResult> processLink(String url)
  • Future<void> logEvent(DetourEventName eventName, {Map<String, dynamic>? data})
  • Future<void> logRetention(String eventName)

Requirements

  • Dart: ^3.11.1
  • Flutter: >=3.3.0
  • Android: min SDK 24
  • iOS: 13.0+

Example

A complete integration example is available in this repo:

  • example/

License

This library is licensed under The MIT License.

Flutter Detour is created by Software Mansion

Since 2012, Software Mansion is a software agency with experience in building web and mobile apps. We are Core React Native Contributors and experts in dealing with all kinds of React Native issues. We can help you build your next dream product - Hire us.

swm