9.
Deep Links & Web URLs
Written by Vincent Ngo
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 Yummy 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 Yummy app on 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 Yummy 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 with 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.
Just imagine, your app Yummy is a user-friendly food app that allows customers to quickly scan a QR code at restaurants, instantly access menus and seamlessly deep-link to detailed restaurant pages in for an enhanced dining experience.
With deep linking, Yummy is more automated. It brings the user directly to the restaurant page making it easier to view the menu. Without deep linking, the process is more manual. The user has to launch the app, navigate to the Explore tab find the correct restaurant, or search the restaurant name, and finally get to the restaurant page to view the menu. 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. yummy://kodeco.com/home is an example of Yummy’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, 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 Yummy 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 diving in, you need to be aware of some new files.
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. These tags have already been added to the starter project.
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>kodeco.com</string>
<key>CFBundleURLSchemes</key>
<array>
<string>yummy</string>
</array>
</dict>
</array>
...
CFBundleURLName is a unique URL that distinguishes your app from others that use the same scheme. yummy 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="yummy"
android:host="kodeco.com" />
</intent-filter>
...
Like in iOS, you set the same values for scheme and host.
When you create a deep link for Yummy, the custom URL scheme looks like this:
yummy://kodeco.com/<path>
Now, take a quick look at the URL paths you’ll create.
Overview of Yummy Paths
Yummy has many screens you can deep link to. Here are all the possible paths you can direct your users to:
Path: /
The app initializes and checks the app cache to see if the user is logged in.
- /login: Redirects to the Login screen if the user isn’t logged in yet.
Path: /:tab
Once the user logs in, 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: /restaurant/:id
The restaurant page is a sub route of the Explore page. You can present a restaurant from any tab.
/restaurant/:id contains one parameter, id.
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:
yummy://kodeco.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 via
BackButtonDispatcher.
-
Router is a widget that extends
RouterDelegate. The router ensures that the messages get to theRouterDelegate. -
Navigator defines a stack of
MaterialPages in a declarative way. It also handles anyonPopPage()event. - 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 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:
GoRouterimplements its ownRouteInformationParsercalled 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 as shown below:
Deep Linking to the Orders Page
Enter the following in your terminal:
xcrun simctl openurl booted 'yummy://kodeco.com/1'
Note: You have to be logged into the app. Otherwise, it will just show the Login page. Note that the first time you run this command the simulator might show a popup. If so, allow it to proceed.
In the simulator, this automatically switches to the second tab, as shown below:
Deep Linking to the Account Page
Next, run the following command:
xcrun simctl openurl booted 'yummy://kodeco.com/2'
This command directs to the Account page:
Deep Linking to the Home Page
Next, run the following command:
xcrun simctl openurl booted 'yummy://kodeco.com/0'
This command directs to the home page, named Explore:
Deep Linking to a Specific Restaurant
Next, run the following command:
xcrun simctl openurl booted 'yummy:/0/restaurant/2'
Observe that the route is structured as /:tab/restaurant/:id. Here, the restaurant page acts as a subroute under the home route. To navigate to a specific restaurant, you need to identify the active tab, followed by the restaurant’s unique ID. This hierarchical routing ensures precise and context-aware navigation within the app.
The restaurant page 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. If you want to reset the cache to see the Login screen again, you have two options:
- Go to the Account view and tap Log out to invalidate the app cache.
-
In the iOS simulator menu, 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. In Android Studio, select an Android emulator or device and click the Run button:
Once the emulator or device is running, log in:
Deep Linking to the Orders Page
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 'yummy://kodeco.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 have
adbin your $PATH. The\at each line’s end formats the script nicely across multiple lines.
This command directs to the second tab of Yummy, that is Orders:
Deep Linking to the Account Page
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 'yummy://kodeco.com/2'
This command navigates to the Account screen:
Deep Linking to Restaurant Page
Next, run the following:
~/Library/Android/sdk/platform-tools/adb shell am start -a android.intent.action.VIEW \
-c android.intent.category.BROWSABLE \
-d 'yummy://kodeco.com/2/restaurant/1'
The selected restaurant page appears, as shown below:
Resetting the Cache in Android
If you need to reset your emulator cache:
In Android Studio go to Tools/Device Manager and you’ll see your list of virtual devices. Click the 3 dotted action bar and select Wipe Data
Now, it’s time to test how Yummy 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 Yummy 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 on the Explore tab, tap on a restaurant
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.
Congratulations on learning how to work with deep links in your Flutter app!
Key Points
- The app notifies
RouteInformationProviderwhen there’s a new route to navigate to. - The provider passes the route information to
RouteInformationParserto parse the URL string. - The parser converts the route information state to and from a URL string.
-
GoRouterconverts route information state to and from aRouteMatchList. -
GoRoutersupports 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 renders 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 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 the 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.