Magic Deeplink
Universal Links & App Links for the Magic Framework.
One unified API for deep linking on iOS and Android — powered by app_links.
Website · Docs · pub.dev · Issues · Discussions
Alpha —
magic_deeplinkis under active development. APIs may change between minor versions until1.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 | |
|---|---|---|
| :link: | Unified API | Single interface for handling deep links on iOS and Android |
| :electric_plug: | Driver Pattern | Extensible driver architecture — swap app_links for any custom driver |
| :traffic_light: | Route Handler | Automatically maps deep link paths to Magic Routes |
| :bell: | OneSignal Integration | Seamless handling of notification click actions via magic_notifications |
| :hammer_and_wrench: | CLI Tools | Auto-generate apple-app-site-association and assetlinks.json |
| :gear: | Config-Driven | All settings in one Dart config file — no platform manifest editing |
| :jigsaw: | Handler Chain | Register custom handlers with canHandle / handle — first match wins |
| :package: | Pure Dart | No native platform code — platform support via app_links package |
Quick Start
1. Add the dependency
dependencies:
magic_deeplink: ^0.0.3
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, it:
- Creates the configured driver (
app_linksby default) - Initializes the driver with your config
- Listens for incoming deep links
- Routes them through your registered handlers
That's it — deep links now work across 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.dartconfig file - Injects
DeeplinkServiceProviderintolib/config/app.dart - Injects
deeplinkConfigfactory intolib/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 |
— | Apple Developer Team ID | |
--bundle-id |
— | iOS app bundle identifier | |
--package-name |
— | Android package name | |
--sha256-fingerprints |
— | 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) async {
final code = uri.pathSegments.last;
// Handle invite code...
return true;
}
}
// In your AppServiceProvider or main.dart:
DeeplinkManager().registerHandler(InviteHandler());
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
→ creates AppLinksDriver
→ driver.initialize(config)
→ listens driver.onLink stream → manager.handleUri()
→ first matching handler wins (canHandle → handle)
→ delays initial link via Future.delayed(Duration.zero) for router readiness
→ optional: OneSignal handler if magic_notifications bound
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.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Write tests following the TDD flow — red, green, refactor
- Ensure all checks pass:
flutter test,dart analyze,dart format . - 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.
Libraries
- cli
- Flutter-free artisan-CLI surface for magic_deeplink.
- magic_deeplink