deep_links_kit 0.1.1
deep_links_kit: ^0.1.1 copied to clipboard
One config for Android App Links and iOS Universal Links: a CLI that sets up the native projects and server files, plus router-agnostic link handling.
deep_links_kit example #
A shop app with go_router, built with Clean Architecture: a splash, a home
page, a product list and product details. Tap myapp://products/42 and the
app opens on that product after the splash, with Home under it so Back works.
Four steps, the same as in the package README. Using auto_route? Only two lines change, see "Using auto_route" below.
1. pubspec.yaml: your links #
deep_links_kit:
hosts: [app.example.com]
scheme: myapp
Then run this. Run it again whenever you change these keys.
dart run deep_links_kit setup
It edits the native projects, so you never touch these files by hand.
Android: in android/app/src/main/AndroidManifest.xml, inside
MainActivity, it adds one block and replaces it on later runs:
<!-- deep_links_kit:start: generated by `dart run deep_links_kit setup` -->
<meta-data
android:name="flutter_deeplinking_enabled"
android:value="false" />
<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="app.example.com" />
</intent-filter>
<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>
<!-- deep_links_kit:end -->
iOS:
ios/Runner/Runner.entitlements:applinks:app.example.comunder Associated Domains. The file is created and linked in the Xcode project when it does not exist yet.ios/Runner/Info.plist: themyappURL scheme.deep_links_kit.lock, next topubspec.yaml: what it added on iOS, so a later run removes only its own entries. Commit this file.
On both, it sets flutter_deeplinking_enabled / FlutterDeepLinkingEnabled
to false. Otherwise Flutter's own deep linking opens every link a second
time.
One change is yours to make. setup warns when MainActivity uses
launchMode="singleTop", the Flutter default. Change it to singleTask in
AndroidManifest.xml. With singleTop, a link tapped in another app starts a
second copy of your app, and the splash runs again.
2. lib/core/deep_links/app_deep_links.dart: links to pages #
All the deep link code of the app is in this file. router is your
GoRouter, from lib/core/router/app_router.dart.
final deepLinks = DeepLinksKit(
navigator: DeepLinkNavigator.custom(push: router.push, replace: router.go),
routes: [
// myapp://products opens the product list
DeepLinkRoute('/products', to: (_) => AppRoutes.products),
// myapp://products/42 opens product 42
DeepLinkRoute('/products/:id', to: (m) => AppRoutes.product(m['id']!)),
// https://app.example.com/p/42, a short link, also opens product 42
DeepLinkRoute('/p/:id', to: (m) => AppRoutes.product(m['id']!)),
],
fallback: (_) => AppRoutes.home, // optional: any other link opens home
);
- Each
DeepLinkRoutesays where a link goes: the link on the left, the page in your app into:. :idis the part of the link you get back asm['id'].fallbackis optional. It is where a link goes when no route matches it, likehttps://app.example.com/unknown. It replaces the open screens, so Back does not return to them. Withoutfallback, such links are ignored and the app stays where it is.
3. lib/main.dart: start listening #
void main() {
WidgetsFlutterBinding.ensureInitialized();
initDependencies();
deepLinks.init(); // before runApp, so the launch link is kept
runApp(const App());
}
4. lib/features/splash/presentation/pages/splash_page.dart: open waiting links #
Links wait until you call ready(), so they never open behind the splash.
Call it right after the splash opens the first screen:
listener: (context, state) {
if (state is SplashDone) {
context.go(AppRoutes.home);
deepLinks.ready(); // links that arrived meanwhile open now
}
},
No splash? Call ready() in main, after the first frame, so the link
opens on top of your first page and Back returns to it:
void main() {
WidgetsFlutterBinding.ensureInitialized();
deepLinks.init();
runApp(const App());
// No splash: open links once the first page is on screen.
WidgetsBinding.instance.addPostFrameCallback((_) => deepLinks.ready());
}
Don't call ready() before runApp: the link would open before the router
has built your first page, and Back would have nothing to return to.
That's it. The rest of the app is ordinary Clean Architecture and knows nothing about deep links:
lib/
main.dart
app.dart
core/
deep_links/ deepLinks (step 2)
di/ get_it, for the features' cubits and repositories
router/ go_router and the page paths
error/ exceptions and failures
usecases/ the UseCase base class
fake_api/ stands in for your backend
widgets/ widgets more than one feature uses
features/
splash/ data / domain / presentation (step 4)
home/ data / domain / presentation
product_list/ data / domain / presentation
product_details/ data / domain / presentation
Using auto_route #
The steps are the same. Only two lines change. A small auto_route app is in
lib/with_auto_route/.
lib/with_auto_route/deep_links.dart #
navigator: DeepLinkNavigator.custom(
push: router.pushPath,
replace: router.navigatePath,
),
lib/with_auto_route/screens.dart #
context.router.replace(const HomeRoute());
deepLinks.ready(); // links that arrived meanwhile open now
lib/with_auto_route/app_router.dart #
Give each route a path:. Those are the paths deep links open:
AutoRoute(page: HomeRoute.page, path: '/home'),
AutoRoute(page: ProductRoute.page, path: '/products/:id'),
AutoRoute(page: UserRoute.page, path: '/users/:id'),
Run this example #
From the GitHub repo, the Android and iOS folders are ready. In the copy from pub.dev they are not included, so create them first. This is also how you try everything from scratch:
flutter create --platforms=android,ios .
rm test/widget_test.dart # flutter create adds it for an app this one is not
dart run deep_links_kit setup
Then change launchMode to singleTask, as in step 1, and run one of the
two apps:
flutter run # go_router, Clean Architecture
flutter run -t lib/main_auto_route.dart # auto_route
Tap the sample links on the home page to open them inside the app. To open a
link from another app, use the share button next to a web link, or on a
product page, and tap the link in WhatsApp or Notes. On Android 11 and older,
pick the app under "Open with". On Android 12 and later, and on iOS, web links
open the app only for a domain you own, with the files from
dart run deep_links_kit generate on it. Put that domain in hosts and in
appLinksHost in lib/core/deep_links/app_deep_links.dart.
You can also send a link from your computer:
adb shell am start -a android.intent.action.VIEW -d "myapp://products/42"
xcrun simctl openurl booted "myapp://products/42"