How to Implement Deep Linking in Flutter Apps

How to Implement Deep Linking in Flutter Apps

Modern mobile applications often need to take users directly to specific content instead of making them navigate through multiple screens. This is where deep linking becomes useful. 

For example, suppose your Flutter app contains a product with ID 123. Instead of asking users to open the app, search for the product, and then open it, you can provide a URL such as: 

https://myapp.com/product/123

When the user taps the link, the application can open directly on the product details screen. 

Deep linking is useful for product pages, user profiles, articles, promotional campaigns, notifications, email links, and shared content. 

In this article, we’ll look at how deep linking works in Flutter and how to configure it for Android and iOS. 

What Is Deep Linking? 

A deep link is a URL that directs a user to a specific location inside an application. For more technical details, see the official Flutter deep linking documentation.

Without deep linking, a user may need to: 

  1. Open the application. 
  2. Navigate to the required section. 
  3. Search for the content. 
  4. Open the desired page. 

With a deep link, the same destination can be opened directly. 

For example: 

https://myapp.com/product/123

This URL could open the profile screen for user 25. 

Deep links are commonly used for: 

  • Product details 
  • User profiles 
  • Blog articles 
  • Orders 
  • Password reset 
  • Email verification 
  • Push notifications 
  • Marketing campaigns 
  • Shared application content 

Types of Deep Links 

There are two common approaches to deep linking. 

Custom URL Schemes 

A custom scheme uses a URL such as: 

myapp.com/product/123

Here, my app is a scheme registered by the application. 

This approach is relatively easy to configure, but custom schemes don’t provide the same domain-based verification as HTTPS links. 

Android App Links and iOS Universal Links 

A more robust approach is to use HTTPS URLs: 

https://myapp.com/product/123

Android Apps can use App Links to associate HTTPS URLs with specific content inside the application, while iOS uses Universal Links.

One advantage is that the same URL can work on the website and open the application when it is installed. 

For production applications, HTTPS-based links are generally useful when you control the associated domain. 

How Deep Linking Works 

The process can be understood as:  

User taps URL
 ↓
 Operating System
 ↓
 Android App Link / iOS Universal Link
 ↓
 Flutter Application
 ↓
 Flutter Router
 ↓
 Target Screen  

For example: 

https://myapp.com/product/123 

The operating system identifies the application associated with the domain. Flutter then receives the route and navigates to the appropriate screen, creating a more direct user experience.

 

Step 1: Create a Flutter Project

If you don’t already have a project, create one:

flutter create deep_link_demo
cd deep_link_demo
flutter run

For this example, we’ll use a home screen and a product details screen. 

First, define a simple route structure: 

MaterialApp(
  initialRoute: '/',
  routes: {
    '/': (context) => const HomeScreen(),
    '/product': (context) => const ProductScreen(),
  },
);

This gives the application a basic navigation structure. 

For applications with dynamic URLs such as /product/123, a routing package can provide a cleaner implementation. 

Step 2: Define Your URL Structure 

Before implementing deep linking, decide how your URLs should look. 

For example: 

https://myapp.com/
https://myapp.com/product/123
https://myapp.com/profile/25
https://myapp.com/article/flutter-deep-linking

A consistent structure makes routing easier to maintain. 

For product pages, you might use: 

/product/123
/product/456
/product/789

The number after /product/ represents the product ID. 

Step 3: Configure Android App Links 

For Android, open: 

android/app/src/main/AndroidManifest.xml

Inside the activity declaration, add an intent filter: 

<intent-filter android:autoVerify="true">
    <action android:name="android.intent.action.VIEW" />

    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />

    <data
        android:scheme="https"
        android:host="myapp.com" />
</intent-filter>

Replace myapp.com with your actual domain. 

The android:autoVerify=”true” setting allows Android to verify that your application is associated with the domain. 

Configure assetlinks.json 

Your website also needs an association file. 

Create: 

https://myapp.com/.well-known/assetlinks.json

A basic example is: 

[
  {
    "relation": [
      "delegate_permission/common.handle_all_urls"
    ],
    "target": {
      "namespace": "android_app",
      "package_name": "com.example.deep_link_demo",
      "sha256_cert_fingerprints": [
        "YOUR_SHA256_CERTIFICATE_FINGERPRINT"
      ]
    }
  }
]

Replace the package name and certificate fingerprint with your application’s actual values. 

Step 4: Configure iOS Universal Links 

For iOS, open the Flutter iOS project in Xcode. 

Go to: 

Signing & Capabilities

Add the Associated Domains capability. 

Then add: 

applinks:myapp.com

Replace the domain with your actual website. 

Configure apple-app-site-association 

Your website also needs an Apple App Site Association file at:
 

https://myapp.com/.well-known/apple-app-site-association

For example: 

{
  "applinks": {
    "details": [
      {
        "appIDs": [
          "TEAM ID.com.example.deep_link_demo"
        ],
        "components": [
          {
            "/": "/product/*"
          }
        ]
      }
    ]
  }
}

The TEAM_ID should be replaced with your Apple Developer Team ID, and the bundle identifier should match your application. 

The /product/* rule allows product URLs to open the application. 

Step 5: Handle Deep Links in Flutter 

For applications with dynamic routes, go_router is a convenient option. 

Install it using: 

flutter pub add go_router

Then define your routes: 

final router = GoRouter(
  routes: [
    GoRoute(
      path: '/',
      builder: (context, state) {
        return const HomeScreen();
      },
    ),
    GoRoute(
      path: '/product/:id',
      builder: (context, state) {
        final productId = state.pathParameters['id'];

        return ProductScreen(
          productId: productId!,
        );
      },
    ),
  ],
);

Use the router with MaterialApp.router: 

MaterialApp.router(
  routerConfig: router,
);

The product screen can receive the ID: 

class ProductScreen extends StatelessWidget {
  final String productId;

  const ProductScreen({
    super.key,
    required this.productId,
  });

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Product Detail'),
      ),
      body: Center(
        child: Text('Product ID: $productId'),
      ),
    );
  }
}

Now the URL: 

https://myapp.com/product/123

can be mapped to: 

/product/123

and Flutter can extract 123 as the product ID. 

Handling Query Parameters 

Deep links can also contain query parameters. 

For example: 

https://myapp.com/product/123?ref=email

Here, 123 is the path parameter and ref=email is a query parameter. 

With go_router, you can access it using:  

final source = state.uri.queryParameters['ref'];

This can be useful for tracking campaigns or identifying where users came from. 

Testing Deep Links 

Testing is an important part of implementing deep linking. 

Test the following scenarios: 

Application Installed 

Open: 

https://myapp.com/product/123

The application should open the product screen. 

Application Closed 

Completely close the application and open the link. Verify that the correct screen appears after startup. 

Application Running 

Keep the application open and trigger another deep link. Make sure it navigates to the new destination correctly. 

Application Not Installed 

Open the link on a device without the application. The user should be directed to your website or another appropriate fallback. 

Invalid URL 

Test URLs with invalid IDs or unsupported paths and make sure the application handles them gracefully instead of crashing. 

Common Deep Linking Issues 

Deep linking may fail even when the Flutter routing code is correct. Some common causes include: 

Incorrect domain: Make sure the domain in your Android and iOS configuration matches your actual domain. 

Wrong certificate fingerprint: Android App Links require the correct SHA-256 certificate fingerprint. Debug and release builds can use different signing certificates. 

Incorrect Team ID: The Apple App Site Association file must contain the correct Team ID and bundle identifier. 

Incorrect file location: Both assetlinks.json and apple-app-site-association must be available from the .well-known directory. 

Missing route: The Flutter router must have a route matching the incoming URL. 

Invalid parameters: Always validate IDs and other parameters received through URLs before using them. 

Deep Linking Best Practices 

A reliable deep-linking implementation should follow a few basic principles: 

  • Keep URL structures simple and meaningful. 
  • Use HTTPS-based links for production applications when appropriate. 
  • Validate all parameters received from URLs. 
  • Provide a fallback for invalid or unavailable content. 
  • Test both Android and iOS devices. 
  • Test cold-start and background scenarios. 
  • Test release builds, not just debug builds. 
  • Keep routing logic separate from business logic. 
  • Make sure website association files are correctly configured. 

Conclusion 

Deep linking provides a direct connection between external URLs and specific screens within a Flutter application. It can significantly reduce the number of steps users need to take when accessing shared content, products, profiles, orders, or promotional pages. 

A complete implementation involves three main areas: 

  1. Platform configuration – Android App Links and iOS Universal Links. 
  2. Domain verification – assetlinks.json for Android and the Apple App Site Association file for iOS. 
  3. Flutter routing – converting incoming URLs into application routes and handling their parameters. 

For simple applications, Flutter’s built-in routing may be enough. For applications with multiple dynamic URLs and complex navigation, a dedicated routing solution such as go_router can make the implementation easier to manage. 

Once the platform configuration and Flutter routing are correctly connected, deep links provide a smooth way to take users directly from a URL to the exact content they need. 

Leave a Reply

Your email address will not be published. Required fields are marked *