Skip to main content

Generate Flutter deep linking config

Enter your domain and the routes that should open from a link. We write the go_router routes, the iOS Info.plist and entitlements, and the Android manifest with the flutter_deeplinking_enabled flag.

Your app

Everything runs in your browser. Nothing you enter is sent to a server.

go_router is Flutter's recommended router. A plain Navigator reads the link in onGenerateRoute.

On is Flutter's default since 3.27: it passes each link to your router. Choose off when a plugin or SDK, such as the WarpLink SDK's onLink, handles links.

One host per line, without https://. These host your Universal Links and App Links.

One per line: a URL path, then the route name. A name such as product opens ProductScreen.

Lowercase, without ://. Leave it empty to use only https links.

In Xcode under Signing & Capabilities. Used to prefill the AASA generator.

Ten characters. Usually your Team ID, in your Apple Developer account under Membership details.

The applicationId in android/app/build.gradle. Used to prefill the assetlinks generator.

Your config

Each file shows where it goes in your project.

router.dart

lib/router.dart (Replace the screen imports with your own screen files.)

import 'package:go_router/go_router.dart';

// Replace these imports with your own screen files.
import 'screens/product_screen.dart';
import 'screens/cart_screen.dart';
import 'screens/home_screen.dart';

final GoRouter router = GoRouter(
  initialLocation: '/',
  routes: <RouteBase>[
    GoRoute(
      path: '/product/:id',
      name: 'product',
      builder: (context, state) => ProductScreen(id: state.pathParameters['id']!),
    ),
    GoRoute(
      path: '/cart',
      name: 'cart',
      builder: (context, state) => const CartScreen(),
    ),
    GoRoute(
      path: '/',
      name: 'home',
      builder: (context, state) => const HomeScreen(),
    ),
  ],
);

main.dart

lib/main.dart

import 'package:flutter/material.dart';
import 'router.dart';

void main() => runApp(MaterialApp.router(routerConfig: router));

Info-deep-linking.xml

ios/Runner/Info.plist (Add inside the top-level dict. Flutter 3.27 and later turn this on by default. Setting it keeps older versions in step.)

  <key>FlutterDeepLinkingEnabled</key>
  <true/>
  <key>CFBundleURLTypes</key>
  <array>
    <dict>
      <key>CFBundleURLSchemes</key>
      <array>
        <string>myapp</string>
      </array>
    </dict>
  </array>

Runner.entitlements

ios/Runner/Runner.entitlements (Or add Associated Domains in Xcode under Signing & Capabilities. Set the file as the target's entitlements.)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>com.apple.developer.associated-domains</key>
  <array>
    <string>applinks:links.example.com</string>
  </array>
</dict>
</plist>

AndroidManifest-deep-linking.xml

android/app/src/main/AndroidManifest.xml (Add inside <activity android:name=".MainActivity">. Flutter 3.27 and later turn this on by default. Setting it keeps older versions in step.)

  <meta-data android:name="flutter_deeplinking_enabled" android:value="true" />
  <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="links.example.com" />
    <data android:pathPrefix="/product/" />
    <data android:path="/cart" />
  </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>

Now generate the files your domain serves

The app config opens your app only after links.example.com serves an AASA file and an assetlinks.json file that name it. Both generators open with this app filled in.

Already serving the files?

The deep link checker fetches both files from links.example.com and reports what a device would read.

How it works

1. Pick your router
go_router is the default. A plain Navigator reads the link in onGenerateRoute.
2. Enter your link domains
Each host becomes an Associated Domains entry on iOS and an intent filter host on Android.
3. Add your routes
Write /product/:id -> product for each screen. A name such as product opens ProductScreen.
4. Choose who handles links
Leave Flutter's handler on, or switch it off when a plugin or SDK processes links itself.

Reference

The fields

Only the domain and the routes are required. The identifiers prefill the domain-file generators.

FieldWhat to enter and where to find it
Routergo_router writes GoRoute entries and a MaterialApp.router. Navigator writes an onGenerateRoute function that matches path segments.
flutter_deeplinking_enabledtrue lets Flutter pass each link to your router, which is the default since Flutter 3.27. false turns Flutter's handler off so a plugin or SDK receives the link.
Link domainsThe hosts that serve your links, one per line, such as links.example.com. No https:// and no path.
RoutesOne /path -> name per line. :id is a parameter. Wildcards are not supported, so list each path. Optional :id? works with go_router only, which gets one route without the value and one with it. A route for / is added when you list none, because the app opens there.
Custom URL schemeOptional. The part before ://, in lowercase. It is registered in Info.plist and the manifest and is not verified.
iOS bundle IDOptional. In Xcode under Signing & Capabilities, or PRODUCT_BUNDLE_IDENTIFIER in the project.
App ID prefixOptional. Ten characters, usually your Team ID under Membership details in your Apple Developer account.
Android package nameOptional. The applicationId in android/app/build.gradle.

Versions these snippets target

ToolVersion
Flutter3.27 and later, where the deep link flag defaults to on. The generator writes the value explicitly, so older versions that need an opt-in also work.
go_router7 and later, which read path parameters from state.pathParameters. Checked against the 18.x API.
iOSUniversal Links through the Associated Domains capability on the Runner target.
AndroidApp Links with android:autoVerify, which Android 6.0 and later check against assetlinks.json.

When a link does not open the right screen

What you seeLikely cause and fix
Page not found or an unknown routeThe path has no matching route. A short link such as /abc123 needs a route that handles it, or the router shows its error page.
A plugin or SDK never receives the linkFlutter's handler is on and took the link first. Set the flag to false in Info.plist and in the manifest.
The app opens but the router ignores the linkWith the flag off, Flutter no longer routes links. Call router.go() with the link your SDK or plugin delivers.
iOS opens Safari, not the appThe entitlement is missing or not attached to the Runner target, or the AASA file does not list your App ID. Check the domain with the deep link checker.
Android shows a chooserVerification failed. assetlinks.json is missing, is served through a redirect, or lists a different package or SHA-256 certificate fingerprint.
The flag has no effect on AndroidThe meta-data element must sit inside the activity element, next to the intent filters, not inside application.

Need deferred deep links and install attribution?

The config on this page opens a screen when the app is installed and a person taps a link. It cannot help the person who taps a link before installing: they go to the store, then open the app with no link in hand.

Deferred deep links, which carry that link through the install, and install attribution need an SDK. The WarpLink Flutter SDK delivers the original link on first open and records which link led to the install. It takes the flag set to false, and its onLink callback then drives your router.

What a Flutter app needs for links

Flutter handles incoming links itself. With go_router, the route paths you declare are matched against the URL the system hands to the app, so one set of routes serves a link, a back stack and a web build. Flutter 3.27 turned this handler on by default.

The platforms still need their own settings. iOS needs the Associated Domains entitlement for each host. Android needs an intent filter with android:autoVerify. Your domain serves the apple-app-site-association and assetlinks.json files that both platforms check, and the generators for those files are linked next to your output. Test on a signed build after each change.

Frequently asked questions

Does go_router handle incoming URLs?

Yes. With Flutter's built-in deep link handling on, go_router receives the URL and matches it against your route paths, including path parameters such as :id.

When do I change Info.plist?

To control Flutter's own link handling with the FlutterDeepLinkingEnabled key, and to register a custom URL scheme. The Associated Domains entitlement is a separate file, one entry for each host.

What does flutter_deeplinking_enabled do?

It decides whether Flutter's handler passes incoming links to your router. It has defaulted to on since Flutter 3.27. Set it to false in Info.plist and in the manifest when a plugin or SDK handles links.

Can a plugin conflict with Flutter's handler?

Yes. With the handler on, Flutter routes each link itself, which can stop a plugin that also listens for links from seeing it. Use one handler. Turn the flag off for a plugin, or leave it on and use go_router.

Should I use go_router or Navigator?

go_router matches URLs, reads path parameters and keeps the back stack in step with the link. A plain Navigator works too: Flutter hands the path to onGenerateRoute, and you match the segments yourself.

How do I test cold starts?

Close the app, then open a link from Notes on iOS or with adb shell am start on Android. Repeat with the app in the background to test the warm path.

Does the domain file replace native settings?

No. The domain files prove your domain trusts the app. The entitlement, Info.plist and manifest settings tell each platform that the app accepts the links.

We handle the link layer for you

Add the WarpLink Flutter SDK for deep links, deferred deep links and install attribution on top of this config.

Start buildingFree to start. No sales call.