Skip to content

Repository files navigation

Magic Logo

Magic Deeplink

Universal Links & App Links for the Magic Framework.
One unified API for deep linking on iOS and Android: powered by app_links.

pub.dev version CI Status License: MIT pub points GitHub Stars

Website · Docs · pub.dev · Issues · Discussions


Alpha: magic_deeplink is under active development. APIs may change between minor versions until 1.0.0.


Why Magic Deeplink?

Setting up deep links in Flutter means dealing with platform-specific manifests, JSON files hosted on your server, parsing URIs in multiple places, and wiring it all together. Every project reinvents the same boilerplate.

Magic Deeplink gives you a single, declarative config file. One CLI command generates the server-side files. One service provider boots everything. Handlers follow a clean chain-of-responsibility pattern: the first match wins.

Config-driven deep linking. Define your domain, paths, and platform credentials once. Magic Deeplink handles the rest.


Features

Feature Description
🔗 Unified API Single interface for handling deep links on iOS and Android
🔌 Driver Pattern Extensible driver architecture: swap app_links for any custom driver
🚥 Route Handler Register RouteDeeplinkHandler with your own path list to map deep link paths to Magic Routes
🔔 OneSignal Integration Seamless handling of notification click actions via magic_notifications
🛠️ CLI Tools Auto-generate apple-app-site-association and assetlinks.json
⚙️ Config-Driven All settings in one Dart config file: no platform manifest editing
🧩 Handler Chain Register custom handlers with canHandle / handle: first match wins
📦 Pure Dart No native Dart plugin code: platform support via app_links package (native project setup is still required, see Installation docs)

Quick Start

1. Add the dependency

dependencies:
  magic_deeplink: ^0.1.0

2. Install configuration

dart run <app>:artisan deeplink:install

This generates lib/config/deeplink.dart, injects DeeplinkServiceProvider into lib/config/app.dart, and wires the deeplinkConfig factory into lib/main.dart.

3. Boot the provider

The DeeplinkServiceProvider is automatically registered during install. On app boot, when deeplink.enabled is not explicitly false, it:

  • Creates the configured driver (app_links by default) when the current platform is supported (Android, iOS, macOS; web and the rest get an explicit no-op)
  • Initializes the driver with your config
  • Listens for incoming deep links, once the first frame has rendered
  • Routes them through your registered handlers

Register a RouteDeeplinkHandler (see Custom Handlers) and deep links route to your app's screens on iOS and Android.


Configuration

After running the install command, edit lib/config/deeplink.dart:

Map<String, dynamic> get deeplinkConfig => {
  'deeplink': {
    'enabled': true,
    'driver': 'app_links',
    'domain': 'example.com',
    'scheme': 'https',
    'ios': {
      'team_id': 'ABC123XYZ',
      'bundle_id': 'com.example.app',
    },
    'android': {
      'package_name': 'com.example.app',
      'sha256_fingerprints': [
        'AA:BB:CC:...',
      ],
    },
    'paths': [
      '/monitors/*',
      '/status-pages/*',
      '/invite/*',
    ],
  },
};

All values are read at runtime via ConfigRepository: no hardcoded strings scattered across your codebase.


Server-Side Setup

Universal Links (iOS) and App Links (Android) require verification files hosted on your domain.

Generate them with one command:

dart run <app>:artisan deeplink:generate --output ./public

This creates two files:

File Platform Purpose
apple-app-site-association iOS Universal Links verification
assetlinks.json Android App Links verification

Upload these to the root or .well-known/ directory of your web server so that https://example.com/.well-known/apple-app-site-association and https://example.com/.well-known/assetlinks.json are publicly accessible.


CLI Tools

install

Scaffolds the deeplink configuration into your Magic project.

dart run <app>:artisan deeplink:install

What it does:

  • Generates lib/config/deeplink.dart config file
  • Injects DeeplinkServiceProvider into lib/config/app.dart
  • Injects deeplinkConfig factory into lib/main.dart
Flag Short Description
--force -f Overwrite existing configuration file

generate

Generates platform verification files from your config.

dart run <app>:artisan deeplink:generate --output ./public

Reads configuration from lib/config/deeplink.dart first. CLI flags override config file values.

Flag Short Default Description
--output -o public Output directory for generated files
--root . Project root directory
--team-id N/A Apple Developer Team ID
--bundle-id N/A iOS app bundle identifier
--package-name N/A Android package name
--sha256-fingerprints N/A SHA-256 certificate fingerprints (multi)
--paths ['/*'] Paths to handle (multi)

Custom Handlers

Register your own handlers for specific deep link patterns:

class InviteHandler extends DeeplinkHandler {
  @override
  bool canHandle(Uri uri) {
    return uri.path.startsWith('/invite/');
  }

  @override
  Future<bool> handle(
    Uri uri, {
    required DeeplinkSource source,
    Map<String, dynamic>? payload,
  }) async {
    final code = uri.pathSegments.last;
    // Handle invite code...
    return true;
  }
}

// In your AppServiceProvider or main.dart:
DeeplinkManager().registerHandler(InviteHandler());

source tells the handler whether the URI arrived as an OS-opened link (DeeplinkSource.osLink, attacker-craftable) or a tapped push notification (DeeplinkSource.push, server-authored, with the full push payload in payload). A handler that acts on more than the path should check it before trusting anything beyond the URI.

Handlers follow the chain-of-responsibility pattern. The first handler where canHandle returns true processes the URI. Return true from handle to indicate success, false to pass to the next handler.


Notification Integration

If magic_notifications is installed and bound in the container, magic_deeplink automatically registers an OneSignalDeeplinkHandler that processes tapped-push actions containing deep link URLs.

No extra configuration needed: the provider detects the binding at boot time and wires everything up. The handler subscribes to the notification manager's own onPushClicked stream, which the manager owns from construction and republishes onto whenever a driver attaches later, so the two packages' providers may register in either order.

To send a deep link via OneSignal, add the url or deep_link field to your notification payload.

Note

This wiring reads NotificationManager.onPushClicked, which ships in magic_notifications 0.1.0. Pairing this release with an older magic_notifications leaves the handler reporting an error instead of routing; see CHANGELOG.md for why the two packages cannot enforce this ordering through their pubspecs.


Architecture

App launch → DeeplinkServiceProvider.boot()
  → reads config via ConfigRepository
  → returns early when deeplink.enabled == false
  → creates AppLinksDriver, when driver.isSupported (web is an explicit no-op)
  → driver.initialize(config)
  → listens driver.onLink stream → manager.handleUri(uri, source: osLink)
  → waits for the first frame's endOfFrame before delivering
  → first matching handler wins (canHandle → handle)
  → optional: OneSignal handler if magic_notifications bound, via manager.handleUri(uri, source: push, payload: data)

Key patterns:

Pattern Implementation
Singleton Manager DeeplinkManager: central orchestrator
Strategy (Driver) AppLinksDriver implements DeeplinkDriver contract
Chain of Responsibility Handlers checked in order: first match wins
Service Provider Two-phase bootstrap: register() (sync) → boot() (async)
IoC Container All bindings via app.singleton() / app.make()

Documentation

Document Description
Installation Adding the package and running the installer
Configuration Config file reference and options
Drivers Driver contract and AppLinksDriver details
Handlers Built-in handlers and writing custom ones
CLI Tools Install and generate command reference
Deeplink Manager Manager singleton and handler orchestration
Service Provider Bootstrap lifecycle and IoC bindings

Contributing

Contributions are welcome! Please see the issues page for open tasks or to report bugs.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Write tests following the TDD flow: red, green, refactor
  4. Ensure all checks pass: flutter test, dart analyze, dart format .
  5. Submit a pull request

License

Magic Deeplink is open-sourced software licensed under the MIT License.


Built with care by FlutterSDK
If Magic Deeplink helps your project, consider giving it a star on GitHub.

About

Deep linking support for Magic Framework (Universal Links, App Links).

Topics

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Contributors

Languages