|
| 1 | +--- |
| 2 | +title: Deep linking |
| 3 | +description: Navigate to routes when the app receives a new URL |
| 4 | +--- |
| 5 | + |
| 6 | +{{site.alert.note}} |
| 7 | + This feature is only available on the dev or master channel. To learn how to |
| 8 | + switch channels, see [Switching Flutter channels][switching-channels]. |
| 9 | +{{site.alert.end}} |
| 10 | + |
| 11 | +Flutter supports deep linking on iOS, Android, and web browsers in the dev |
| 12 | +channel. Opening a URL displays that screen in your app. With the following |
| 13 | +steps, you can launch and display routes by using named routes (either with the |
| 14 | +[`routes`][routes] parameter or [`onGenerateRoute`][onGenerateRoute]), or by |
| 15 | +using the [`Router`][Router] widget. |
| 16 | + |
| 17 | +{{site.alert.secondary}} |
| 18 | + **Version note:** [Navigator 2.0][] is now called [`Router`][Router], which |
| 19 | + allows you to declaratively set the displayed routes based on the app's |
| 20 | + current state. This API is opt-in. |
| 21 | +{{site.alert.end}} |
| 22 | + |
| 23 | +If you're running the app in a web browser, there's no additional setup |
| 24 | +required. Route paths are handled in the same way as an iOS or Android deep |
| 25 | +link. By default, web apps read the deep link path from the url fragment |
| 26 | +using the pattern: `/#/path/to/app/screen`. |
| 27 | + |
| 28 | +To follow along, create a new Flutter project with [the `Router` widget |
| 29 | +sample][router-sample] in flutter/samples. |
| 30 | + |
| 31 | +## Enable deep linking on Android |
| 32 | + |
| 33 | +Add a metadata tag and [intent filter][] to `AndroidManifest.xml` |
| 34 | +inside the `<activity> `tag with the `".MainActivity"` name: |
| 35 | + |
| 36 | +``` |
| 37 | +<!-- Deep linking --> |
| 38 | +<meta-data android:name="flutter_deeplinking_enabled" android:value="true" /> |
| 39 | +<intent-filter android:autoVerify="true"> |
| 40 | + <action android:name="android.intent.action.VIEW" /> |
| 41 | + <category android:name="android.intent.category.DEFAULT" /> |
| 42 | + <category android:name="android.intent.category.BROWSABLE" /> |
| 43 | + <data android:scheme="http" android:host="flutterbooksample.com" /> |
| 44 | + <data android:scheme="https" /> |
| 45 | +</intent-filter> |
| 46 | +``` |
| 47 | + |
| 48 | +A full restart is required to apply these changes. |
| 49 | + |
| 50 | +## Test on Android emulator |
| 51 | +To test with an Android emulator, give the `adb` command an intent where the |
| 52 | +host name matches the name defined in `AndroidManifest.xml`: |
| 53 | + |
| 54 | +``` |
| 55 | +adb shell am start -a android.intent.action.VIEW \ |
| 56 | + -c android.intent.category.BROWSABLE \ |
| 57 | + -d "http://flutterbooksample.com/book/1" |
| 58 | +``` |
| 59 | + |
| 60 | +For more details, see the [Verify Android App Links][verify-android-links] |
| 61 | +documentation in the Android docs. |
| 62 | + |
| 63 | +## Enable deep linking on iOS |
| 64 | +Add two new keys to `Info.plist` in the ios/Runner directory: |
| 65 | + |
| 66 | +``` |
| 67 | +<key>FlutterDeepLinkingEnabled</key> |
| 68 | +<true/> |
| 69 | +<key>CFBundleURLTypes</key> |
| 70 | +<array> |
| 71 | + <dict> |
| 72 | + <key>CFBundleTypeRole</key> |
| 73 | + <string>Editor</string> |
| 74 | + <key>CFBundleURLName</key> |
| 75 | + <string>flutterbooksample.com</string> |
| 76 | + <key>CFBundleURLSchemes</key> |
| 77 | + <array> |
| 78 | + <string>customscheme</string> |
| 79 | + </array> |
| 80 | + </dict> |
| 81 | +</array> |
| 82 | +``` |
| 83 | + |
| 84 | +The `CFBundleURLName` is a unique URL used to distinguish your app from others |
| 85 | +that use the same scheme. The scheme (`customscheme://`) can also be unique. |
| 86 | + |
| 87 | +A full restart is required to apply these changes. |
| 88 | + |
| 89 | +## Test on iOS simulator |
| 90 | +Use the `xcrun` command to test on the iOS Simulator: |
| 91 | + |
| 92 | +``` |
| 93 | +xcrun simctl openurl booted customscheme://flutterbooksample.com/book/1 |
| 94 | +``` |
| 95 | + |
| 96 | +## Migrating from plugin-based deep linking |
| 97 | + |
| 98 | +If you have written a plugin to handle deep links, as described in ["Deep Links |
| 99 | +and Flutter applications" on Medium][plugin-linking], it will continue to work |
| 100 | +until you opt-in to this behavior by adding `FlutterDeepLinkingEnabled` to |
| 101 | +`Info.plist` or `flutter_deeplinking_enabled` to `AndroidManifest.xml`, |
| 102 | +respectively. |
| 103 | + |
| 104 | +## Behavior |
| 105 | + |
| 106 | +The behavior varies slightly based on the platform and whether the app is |
| 107 | +launched and running. |
| 108 | + |
| 109 | +<div class="table-wrapper" markdown="1"> |
| 110 | +| Platform / Scenario | Using Navigator | Using Router | |
| 111 | +|--------------------------|---------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |
| 112 | +| iOS (not launched) | App gets initialRoute ("/") and a short time after gets a pushRoute | App gets initialRoute ("/") and a short time after uses the RouteInformationParser to parse the route and call RouterDelegate.setNewRoutePath, which configures the Navigator with the corresponding Page. | |
| 113 | +| Android - (not launched) | App gets initialRoute containing the full route ("http:/deeplink/") | App gets initialRoute ("/deeplink") and passes it to the RouteInformationParser to parse the route and call RouterDelegate.setNewRoutePath, which configures the Navigator with the corresponding Pages. | |
| 114 | +| iOS (launched) | pushRoute is called | Path is parsed, and the Navigator is configured with a new set of Pages. | |
| 115 | +| Android (launched) | pushRoute is called | Path is parsed, and the Navigator is configured with a new set of Pages. | |
| 116 | +{:.table.table-striped} |
| 117 | +</div> |
| 118 | + |
| 119 | +After upgrading to the [`Router`][Router] widget, your app has the ability to replace the |
| 120 | +current set of pages when a new deep link is opened while the app is running. |
| 121 | + |
| 122 | +[switching-channels]: /docs/development/tools/sdk/upgrading#switching-flutter-channels |
| 123 | +[routes]: https://api.flutter.dev/flutter/material/MaterialApp/routes.html |
| 124 | +[onGenerateRoute]: https://api.flutter.dev/flutter/material/MaterialApp/onGenerateRoute.html |
| 125 | +[Router]: https://api.flutter.dev/flutter/widgets/Router-class.html |
| 126 | +[Navigator 2.0]: https://medium.com/flutter/learning-flutters-new-navigation-and-routing-system-7c9068155ade |
| 127 | +[intent filter]: https://developer.android.com/guide/components/intents-filters |
| 128 | +[plugin-linking]: https://medium.com/flutter-community/deep-links-and-flutter-applications-how-to-handle-them-properly-8c9865af9283 |
| 129 | +[verify-android-links]: https://developer.android.com/training/app-links/verify-site-associations |
| 130 | +[router-sample]: https://github.com/flutter/samples/blob/master/navigation_and_routing/lib/nav_2/router.dart |
0 commit comments