跳转至

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:

img

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.

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.

img

  • 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!

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.

img

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.

img

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.

To enable deep linking on iOS and Android, you must add metadata tags on the respective platforms.

You’ll start with 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.

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.

img

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.

img

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.

img

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:

  1. If query parameter id has a value, it’ll redirect to a specific item in the list.
  2. 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.

img

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:

img

  • 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 pass to RouterDelegate.
  • Navigator defines a stack of MaterialPages in a declarative way. It also handles any onPopPage() 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.

img

  • 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 own RouteInformationParser called GoRouteInformationParser. Based on the routeInformation, 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!

Next, you’ll test how deep linking works on iOS, Android and the web.

In Android Studio, select an iOS device and press Run:

img

Once the simulator is running, log in and complete the onboarding, as shown below:

img

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:

img

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:

img

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:

img

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:

  1. Go to the Profile view and tap Log out to invalidate the app cache.

img

  1. 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.

img

Stop running on iOS. Open Android Studio, select an Android device and click Play:

img

Once the simulator or device is running, log in and complete the onboarding process:

img

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 have adb 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:

img

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:

img

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:

img

Resetting the Cache in Android

If you need to reset your user cache:

img

  1. Long-press the Fooderlich app icon, then tap App info.
  2. Next, tap Storage & cache.
  3. 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:

img

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:

img

If you change the tab query parameter’s value to 0, 1 or 2, the app automatically switches to that tab.

img

Next, tap the + button to open the grocery item.

img

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.

img

Next, tap the user’s profile on the top right. Then tap View raywenderlich.com.

img

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 a RouteMatchList.
  • 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.