8 Deep Links & Web URLs¶
Sometimes, opening your app and working through the navigation to get to a screen is just too much trouble for the user. Redirecting to a specific part of your app is a powerful marketing tool for user engagement. For example, generating a special QR code for a promotion that users can scan to visit that specific product in your app is a cool and effective way to build interest in the product.
In the last chapter, you learned how to use GoRouter
to move between screens, navigating your app in a declarative way. Now you’ll learn how to deep link to screens in your app and explore web URLs on the web.
Take a look at how Fooderlich looks in the Chrome web browser:
By the end of this chapter, you’ll:
- Have a better understanding of the router API.
- Know how to support deep linking on iOS and Android.
- Explore the Fooderlich app on the web.
You’ll also explore deep links on iOS, Android and the web. You’ll learn how to direct users to any screen of your choice.
Note: You’ll need to install the Chrome web browser to view Fooderlich on the web. If you don’t have Chrome, you can get it here. The Flutter web project can run on other browsers, but this chapter only covers testing and development on Chrome.
Understanding Deep Links¶
A deep link is a URL that navigates to a specific destination in your mobile app. Think of deep links like a URL address you enter into a web browser to go to a specific page of a website rather than the home page.
Deep links help with user engagement and business marketing. For example, if you’re running a sale, you can direct the user to a specific product page in your app instead of making them search for it.
Imagine that Fooderlich has a website. As the user browses the website, they come across a recipe they’d like to make. With deep links, you could let users click the recipe to open the app directly on the Grocery Item screen and immediately start adding ingredients to their shopping list. This saves them time and makes the app more enjoyable.
- With deep linking, Fooderlich is more automated. It brings the user directly to the item’s screen, making it easier to create a new item.
- Without deep linking, the process is more manual. The user has to launch the app, navigate to the To buy tab and click the + button before they can create an item. That takes three steps instead of one and likely some head-scratching, too!
Types of Deep Links¶
There are three types of deep links:
- URI schemes: An app’s own URI scheme. fooderlich://raywenderlich.com/home is an example of Fooderlich’s URI scheme. This form of deep link only works if the user has installed your app.
- iOS Universal Links: In the root of your web domain, you place a file that points to a specific app ID to say whether to open your app or to direct the user to the App Store. You must register that specific app ID with Apple to handle links from that domain.
- Android App Links: Like iOS Universal Links for the Android platform, Android App Links take users to a link’s specific content directly in your app. They leverage HTTP URLs and are associated with a website. For users that don’t have your app installed, these links go directly to the content of your website.
In this chapter, you’ll only look at URI Schemes. For more information on how to set up iOS Universal Links and Android App Links, check out these tutorials:
Getting Started¶
Note: We recommend you use the starter project for this chapter rather than continuing with the project from the last chapter.
Open the starter project in Android Studio and run flutter pub get
. Then, run the app on iOS or Android.
You’ll see that Fooderlich shows the Login screen.
Soon, you’ll be able to redirect users to different parts of the app. But first, take a moment to review what’s changed in the starter project since the last chapter.
Project Files¶
Before you dive into parsing URLs, you need to be aware of some files.
Screens Folder¶
Within lib/screens/:
- profile_screen.dart: Handles two different cases when the user opens raywenderlich.com.If the user is on mobile, it opens the website in a web view.If the user is on a web browser, it opens the website in a different tab.
New Flutter Web Project¶
The starter project includes a pre-built Flutter web project.
Note: To speed things up, the web project is pre-built in your starter project. To learn how to create a Flutter web app, check out the Flutter documentation.
Setting Up Deep Links¶
To enable deep linking on iOS and Android, you must add metadata tags on the respective platforms.
You’ll start with iOS.
Setting Up Deep Links on iOS¶
Open ios/Runner/Info.plist. You’ll see some new key-value pairs, which enable deep linking for iOS:
<key>FlutterDeepLinkingEnabled</key>
<true/>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>raywenderlich.com</string>
<key>CFBundleURLSchemes</key>
<array>
<string>fooderlich</string>
</array>
</dict>
</array>
CFBundleURLName
is a unique URL that distinguishes your app from others that use the same scheme. fooderlich
is the URL scheme you’ll use later.
Setting Up Deep Links on Android¶
Open android/app/src/main/AndroidManifest.xml. Here you’ll also find two new definitions in the <data>
tag:
<!-- Deep linking -->
<meta-data android:name="flutter_deeplinking_enabled" android:value="true" />
<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="fooderlich"
android:host="raywenderlich.com" />
</intent-filter>
Like iOS, you set the same values for scheme
and host
.
When you create a deep link for Fooderlich, the custom URL scheme looks like this:
fooderlich://raywenderlich.com/<path>
Now, take a quick look at the URL paths you’ll create.
Overview of Fooderlich’s Paths¶
Fooderlich has many screens you can deep link to. Here are all the possible paths you can redirect your users to:
Path: /¶
The app initializes and checks the app cache to see if the user is logged in and has completed the onboarding guide.
- /login: Redirects to the Login screen if the user isn’t logged in yet.
- /onboarding: Redirects to the Onboarding screen if the user hasn’t completed the onboarding.
Path: /:tab¶
Once the user logs in and completes onboarding, they’re redirected to /:tab
. It contains one parameter, tab
, which directs to a tab index. The screenshots below show that the tab index is 0
, 1
or 2
, respectively.
Path: /:tab/profile¶
The profile route is actually a sub route of home. From the home screen, the user can open their user profile from each of the three tabs.
Path: /:tab/item/:id¶
The item screen is also a sub route of home. You can present an item from any tab.
/item/:id
contains one query parameter, id
. There are two scenarios:
- If query parameter
id
has a value, it’ll redirect to a specific item in the list. - If there is no query parameter, it shows an empty item screen for the user to create a new item.
You’ll see the result in the screenshots below.
Note: Keep in mind that these URL paths work similarly for mobile and web apps.
When you deep link on mobile, you’ll use the following URI scheme:
fooderlich://raywenderlich.com/<path>
On the web, the URI scheme is like any web browser URL:
http://localhost:60738/#/<path>
Before exploring deep links, take a moment for a quick Router API recap.
Router API Recap¶
In the last chapter, you learned how to use GoRouter
to set up routes and navigate to screens. GoRouter
conveniently manages the Router API for you. How amazing is that? :]
However, it’s still good to understand how routing works behind the scenes. Here’s a diagram of what makes up the Router API:
RouterDelegate
’s responsibilities include:Using App State to build and configure the list of pages.Retrieving and setting up the initial route when the app first launches.Listening for new intents when you show a new route.Listening to requests by the operating system to pop a route viaBackButtonDispatcher
.- Router is a widget that extends
RouterDelegate
. The router ensures that the messages pass toRouterDelegate
. - Navigator defines a stack of
MaterialPage
s in a declarative way. It also handles anyonPopPage()
events. - BackButtonDispatcher handles platform-specific system back button presses. It listens to requests by the OS and tells the router delegate to pop a route.
Next, you’ll look at RouteInformationProvider and RouteInformationParser.
- RouteInformationProvider: Provides the route information to the router. It informs the router about the initial route and notifies it of new intents.
- RouteInformationParser: Gets the route string from
RouteInformationProvider
, then parses the URL string to a generic user-defined data type. This data type is a navigation configuration.
Note:
GoRouter
implements it’s ownRouteInformationParser
called GoRouteInformationParser. Based on therouteInformation
, it tries to search for a route match based on the route’s location. Check out the code in this GitHub repository.
Since GoRouter
provides and manages all of these components, it’s a good idea to jump straight into GoRouter
’s implementation to learn more and see how they configure things.
Enough theory. It’s time to get started!
Testing Deep Links¶
Next, you’ll test how deep linking works on iOS, Android and the web.
Testing Deep Links on iOS¶
In Android Studio, select an iOS device and press Run:
Once the simulator is running, log in and complete the onboarding, as shown below:
Deep Linking to the Home Screen¶
Enter the following in your terminal:
xcrun simctl openurl booted 'fooderlich://raywenderlich.com/1'
Note: Entering this command in Android Studio’s terminal may cause a popup on the simulator. If so, allow it to proceed.
In the simulator, this automatically switches to Fooderlich’s second tab, as shown below:
Deep Linking to the Profile Screen¶
Next, run the following command:
xcrun simctl openurl booted 'fooderlich://raywenderlich.com/1/profile'
This command opens the Profile screen:
Deep Linking to Create a New Item¶
Next, run the following command:
xcrun simctl openurl booted 'fooderlich://raywenderlich.com/2/item/new'
The Grocery Item screen will now show:
Following this pattern, you can build paths to any location in your app!
Resetting the Cache in the iOS Simulator¶
Recall that AppStateManager
checks with AppCache
to see whether the user is logged in or has onboarded. If you want to reset the cache to see the Login screen again, you have two options:
- Go to the Profile view and tap Log out to invalidate the app cache.
- If you’re running on an iOS simulator, you can select Erase All Content and Settings… to clear the cache.
Note: This will delete any other apps you have on the simulator.
Testing Deep Links on Android¶
Stop running on iOS. Open Android Studio, select an Android device and click Play:
Once the simulator or device is running, log in and complete the onboarding process:
Deep Linking to the Home Screen¶
Enter the following in your terminal:
~/Library/Android/sdk/platform-tools/adb shell am start -a android.intent.action.VIEW \
-c android.intent.category.BROWSABLE \
-d 'fooderlich://raywenderlich.com/1'
Note: If you receive a message in Terminal like:
Warning: Activity not started, intent has been delivered to currently running top-most instance
, ignore it. It just means that the app is already running. The entire path is listed to ensure that you can still execute this command if you don’t haveadb
in your $PATH. The\
at each line’s end formats the script nicely across multiple lines.
This command directs to the second tab of Fooderlich:
Deep Linking to the Profile Screen¶
Next, run the following command:
~/Library/Android/sdk/platform-tools/adb shell am start -a android.intent.action.VIEW \
-c android.intent.category.BROWSABLE \
-d 'fooderlich://raywenderlich.com/1/profile'
This command opens the Profile screen:
Deep Linking to Create New Item¶
Next, run the following command:
~/Library/Android/sdk/platform-tools/adb shell am start -a android.intent.action.VIEW \
-c android.intent.category.BROWSABLE \
-d 'fooderlich://raywenderlich.com/1/item/new'
The Grocery Item screen appears, as shown below:
Resetting the Cache in Android¶
If you need to reset your user cache:
- Long-press the Fooderlich app icon, then tap App info.
- Next, tap Storage & cache.
- Finally, tap Clear cache to wipe your cache.
Now, it’s time to test how Fooderlich handles URLs on the web.
Running the Web App¶
Stop running on Android. In Android Studio, select Chrome (web) and click Run:
Note: Your data won’t persist between app launches because Flutter web runs the equivalent of incognito mode during development. If you build and release your Flutter web app, it’ll work as expected. For more information on how to build for release, check the Flutter documentation.
Go through the Fooderlich UI flow, and you’ll see that the web browser’s address bar changes:
If you change the tab
query parameter’s value to 0, 1 or 2, the app automatically switches to that tab.
Next, tap the + button to open the grocery item.
Notice that the app stores the entire browser history. Pretty cool.
Tap the Back and Forward buttons, and the app restores that state! How cool is that? You can also long-press the Back button to jump to a specific state in the browser history.
Next, tap the user’s profile on the top right. Then tap View raywenderlich.com.
Notice it opens the web link on another tab!
Congratulations on learning how to work with deep links in your Flutter app!
Key Points¶
- The app notifies
RouteInformationProvider
when there’s a new route. - The provider passes the route information to
RouteInformationParser
to parse the URL string. - The parser converts route information state to and from a URL string.
GoRouter
converts route information state to and from aRouteMatchList
.GoRouter
supports deep linking and web browser address bar paths out of the box.- In development mode, the Flutter web app doesn’t persist data between app launches. The web app generated in release mode will work on other browsers.
Where to Go From Here?¶
Flutter render’s web apps in two different ways. Explore how that works in the Flutter documentation on web renderers.
For more examples of various navigation use-cases with GoRouter
, check out these GoRouter
examples.
In this chapter, you continued to learn how the Router API works behind the scenes and explore how to test and perform deep links on iOS, Android and Web.
Deep linking helps bring users to specific destinations within your app, building better user engagement!
Flutter’s ability to support routes and navigation for multiple platforms isn’t just powerful; it’s magical.