# Deep Linking Setup Use this reference for Android App Links, iOS Universal Links, custom URI schemes, and deep-link validation. Prefer verified `https` links for production links that should open the app from the web. ## Routing Model Flutter can deliver incoming route information to `Navigator` or Router-based apps. For new deep-link work, prefer `go_router` or another Router-based solution so an incoming URL maps to a predictable page stack. ```dart final router = GoRouter( routes: [ GoRoute(path: '/', builder: (context, state) => const HomeScreen()), GoRoute( path: '/products/:productId', builder: (context, state) { final productId = state.pathParameters['productId']!; return ProductScreen(productId: productId); }, ), ], ); ``` If the app uses a third-party deep-link plugin, confirm whether Flutter's default deep-link handler should be disabled. In recent Flutter versions it is enabled by default; plugin-based handlers usually require opting out in platform configuration. ## Android App Links Android App Links use `http` or `https` links verified against a domain you own. ### AndroidManifest.xml Add an intent filter to the activity that should receive the link: ```xml ``` For custom schemes, use a scheme such as `myapp` instead of `https`, but expect weaker verification and possible scheme conflicts. ```xml ``` ### assetlinks.json Host the association file at: ```text https://example.com/.well-known/assetlinks.json ``` Example: ```json [ { "relation": ["delegate_permission/common.handle_all_urls"], "target": { "namespace": "android_app", "package_name": "com.example.app", "sha256_cert_fingerprints": ["SHA256_FINGERPRINT"] } } ] ``` Use the release signing fingerprint for production builds and include debug fingerprints only for debug validation. ### Android Tests ```bash adb shell am start -W -a android.intent.action.VIEW -d "https://example.com/products/42" adb shell am start -W -a android.intent.action.VIEW -d "myapp://products/42" ``` ## iOS Universal Links iOS Universal Links use `http` or `https` and require Associated Domains. They are not configured by putting the Associated Domains entitlement in `Info.plist`. ### Associated Domains Add the Associated Domains capability in Xcode, or edit `ios/Runner/Runner.entitlements`: ```xml com.apple.developer.associated-domains applinks:example.com ``` Personal development teams might not support Associated Domains. Confirm the target signing team before treating Universal Links as verified. ### apple-app-site-association Host the association file with no `.json` extension: ```text https://example.com/.well-known/apple-app-site-association ``` Example: ```json { "applinks": { "apps": [], "details": [ { "appIDs": ["TEAMID.com.example.app"], "paths": ["*"], "components": [ { "/": "/*" } ] } ] } } ``` Apple's CDN can delay Universal Link validation. Account for that delay when debugging fresh domain changes. ### iOS Custom Schemes Use `Info.plist` for custom schemes: ```xml CFBundleURLTypes CFBundleURLName com.example.app CFBundleURLSchemes myapp ``` Custom schemes are useful for app-only flows, but they are not domain-verified like Universal Links. ### iOS Tests ```bash xcrun simctl openurl booted "https://example.com/products/42" xcrun simctl openurl booted "myapp://products/42" ``` ## Web Deep Links Flutter web handles URL paths in the browser. With the default hash strategy, links look like: ```text https://example.com/#/products/42 ``` With path strategy, links look like: ```text https://example.com/products/42 ``` Path strategy requires server rewrites to `index.html`. See [web-navigation.md](web-navigation.md). ## Validation Checklist - Route table includes the expected incoming path. - Required path parameters are parsed from `state.pathParameters`. - Optional query values are parsed from `state.uri.queryParameters`. - Invalid or missing values show an error screen, redirect, or safe fallback. - Android manifest filters match scheme, host, and path expectations. - `assetlinks.json` uses the correct package name and signing fingerprint. - iOS Associated Domains are in Xcode capabilities or `Runner.entitlements`. - The AASA file is reachable with the correct domain, team ID, and bundle ID. - Third-party link plugins are not fighting Flutter's default handler. - Device or simulator commands were run for the exact URLs changed.