deep_link_manager 0.4.0 copy "deep_link_manager: ^0.4.0" to clipboard
deep_link_manager: ^0.4.0 copied to clipboard

A generic deep link management package for Flutter.

Deep Link Feature Documentation #

Overview #

The Deep Link feature provides a robust, generic architecture for handling deep links (Universal Links, App Links, Custom Schemes) in Flutter applications. It decouples the deep link handling logic from the main app flow and provides centralized management for authentication requirements.

This package is built on top of the excellent app_links package, providing a higher-level abstraction for strategy-based link handling and authentication guards.

Core Components #

  1. DeepLinkManager: A singleton that listens for incoming links (initial & stream), manages application readiness state (isAppReady), handles queued links with expiration, and guards against race conditions.
  2. DeepLinkStrategy: An abstract class that you implement to define how to handle specific types of links. Includes priority ordering and unique identifiers.
  3. DeepLinkAuthProvider: A typed interface for secure authentication checks.

๐Ÿ“ฑ App Configuration #

To enable your app to "catch" these links, you must configure the iOS and Android projects.

1. iOS Configuration #

File: ios/Runner/Info.plist

Add/Merge the CFBundleURLTypes for custom schemes:

<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleTypeRole</key>
    <string>Editor</string>
    <key>CFBundleURLName</key>
    <string><package_name></string>
    <key>CFBundleURLSchemes</key>
    <array>
      <string><scheme></string>
    </array>
  </dict>
</array>
  • <package_name>: Example: com.example.micropet
  • <scheme>: Example: micropet

Universal Links (Entitlements):

  1. Open ios/Runner.xcworkspace in Xcode.
  2. Go to Signing & Capabilities.
  3. Add Associated Domains.
  4. Add your domain with the applinks: prefix: applinks:<host>.

Note (Flutter 3.24+): You MUST add the following to Info.plist to disable Flutter's default deep link handling:

<key>FlutterDeepLinkingEnabled</key>
<false/>

2. Android Configuration #

File: android/app/src/main/AndroidManifest.xml

Add intent-filter entries inside the main <activity>:

<!-- Custom Scheme (<scheme>://) -->
<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="<scheme>" android:host="<host>" />
</intent-filter>

<!-- App Links (https://...) -->
<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="<host>" />
</intent-filter>

<meta-data android:name="flutter_deeplinking_enabled" android:value="false" />
  • <scheme>: Example: micropet
  • <host>: Example: micropet-web.vercel.app

Note (Flutter 3.24+): You MUST add the flutter_deeplinking_enabled metadata with value false to disable Flutter's default deep link handling, which conflicts with this package.

3. Android 13+ Debugging #

On Android 13 and newer, App Links may not be automatically enabled in debug builds.

  • Go to App Info (Long press app icon > App Info).
  • Select Open by default.
  • Tap Add link and select your supported specific links.

๐Ÿงช Testing #

Android (ADB) #

You can test deep links without a web page using ADB:

# Custom Scheme
adb shell am start -a android.intent.action.VIEW \
  -d "micropet://product/123"

# App Link
adb shell am start -a android.intent.action.VIEW \
  -d "https://micropet-web.vercel.app/product/123"

iOS (Simulator) #

You can test on the iOS simulator using xcrun:

# Custom Scheme
xcrun simctl openurl booted "micropet://product/123"

# Universal Link
xcrun simctl openurl booted "https://micropet-web.vercel.app/product/123"

๐Ÿš€ Quick Setup Guide #

1. Dependencies #

Ensure app_links is added to your pubspec.yaml:

dependencies:
  app_links: ^6.3.1 # check for latest version

2. Create a Strategy #

Implement DeepLinkStrategy to define your custom handling logic.

import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import '../domain/deep_link_strategy.dart';

class MyDeepLinkStrategy implements DeepLinkStrategy<String> {
  @override
  String get identifier => 'MyDeepLinkStrategy';

  @override
  int get priority => 10; // Higher = processed first

  @override
  bool canHandle(Uri uri) {
    return uri.pathSegments.isNotEmpty && uri.pathSegments[0] == 'product';
  }

  @override
  String? extractData(Uri uri) {
    if (uri.pathSegments.length > 1) return uri.pathSegments[1];
    return null;
  }

  @override
  bool get requiresAuth => true;

  @override
  void handle(Uri uri, BuildContext context, String? data) {
    if (data == null || data.isEmpty) return;
    GoRouter.of(context).push('/product/$data');
  }
}

3. Create an Auth Provider #

Implement DeepLinkAuthProvider for typed authentication handling.

class AppDeepLinkAuthProvider implements DeepLinkAuthProvider {
  final GlobalKey<NavigatorState> _navigatorKey;

  AppDeepLinkAuthProvider(this._navigatorKey);

  @override
  bool get isAuthenticated {
    final context = _navigatorKey.currentContext;
    if (context != null) {
      return context.read<AuthBloc>().state is AuthenticatedState;
    }
    return false;
  }

  @override
  void onAuthRequired(Uri uri) {
    final context = _navigatorKey.currentContext;
    if (context != null) {
      GoRouter.of(context).go('/login');
    }
  }
}

4. Initialize in main.dart #

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  final deepLinkManager = DeepLinkManager();
  await deepLinkManager.initialize(
    strategies: [
      MyDeepLinkStrategy(),
    ],
    authProvider: AppDeepLinkAuthProvider(deepLinkManager.navigatorKey),
  );

  runApp(MyApp());
}

5. Router Configuration #

Use the navigatorKey from DeepLinkManager in your Router config. Alternatively, you can pass your own navigatorKey to initialize.

final _router = GoRouter(
  navigatorKey: deepLinkManager.navigatorKey, // <--- Use the same instance
  // ...
);

6. Handle App Readiness (Splash Screen) #

The Manager queues links until the app is "ready". You MUST call setAppReady() when ready.

void onSplashFinished() {
  DeepLinkManager().setAppReady();
}

7. Handling Authentication #

If a deep link requires authentication, it will be queued until the user logs in. After a successful login, call checkPendingLinks() to process any waiting links.

// In your Login Bloc/Provider
void onLoginSuccess() {
  DeepLinkManager().checkPendingLinks();
}

API Reference #

DeepLinkManager #

Method Description
registerStrategy(DeepLinkStrategy) Add a strategy (sorted by priority)
initialize({strategies, authProvider, navigatorKey}) Start listener with components
setAppReady() Signal UI ready, process pending links
checkPendingLinks() Manually trigger processing (e.g., after login)
clearPendingLink() Clear pending links (e.g., on logout)
navigatorKey GlobalKey for Navigation (injected or default)
hasPendingLink Check if a link is queued
isInitialized Check if initialization completed

Behaviors:

  • Link Expiration: Pending links expire after 5 minutes
  • Race Protection: Guards against re-entrant processing
  • Error Handling: Strategy execution is wrapped in try-catch

DeepLinkStrategy<T> #

Property/Method Description
identifier Unique string for logging (required)
priority Ordering priority; higher = first (default: 0)
canHandle(Uri) Returns true if this strategy handles the URI
extractData(Uri) Extract data of type T passed to handle
requiresAuth Return true if user must be logged in
handle(Uri, BuildContext, T?) Perform navigation with typed data

DeepLinkAuthProvider #

Property/Method Description
isAuthenticated Returns true if user is logged in
onAuthRequired(Uri) Called when auth is required but missing

1. iOS Configuration (apple-app-site-association) #

Host at https://<your-domain>/.well-known/apple-app-site-association:

{
  "applinks": {
    "apps": [],
    "details": [
      {
        "appID": "<TeamID>.<BundleID>",
        "paths": [ "/pet/*", "/product/*" ]
      }
    ]
  }
}

2. Android Configuration (assetlinks.json) #

Host at https://<your-domain>/.well-known/assetlinks.json:

[
  {
    "relation": ["delegate_permission/common.handle_all_urls"],
    "target": {
      "namespace": "android_app",
      "package_name": "<package_name>",
      "sha256_cert_fingerprints": ["<SHA256_FINGERPRINT>"]
    }
  }
]

3. Verification #

  • Files accessible via HTTPS without redirects
  • Content-Type: application/json
1
likes
160
points
210
downloads

Publisher

unverified uploader

Weekly Downloads

A generic deep link management package for Flutter.

Repository (GitHub)
View/report issues

Topics

#deep-linking #navigation #strategy-pattern #routing

Documentation

API reference

License

MIT (license)

Dependencies

app_links, flutter

More

Packages that depend on deep_link_manager