Skip to content
Blog

Deep Linking and App Links in 2026

Learn how to implement deep links and Android App Links so users land in the right screen of your app, verified and secure.

Published on August 17, 2026

AI Assistant

A notification says “New message from Priya” and tapping it should open your app directly on the chat thread — not the home screen, not a sign-in wall. That’s deep linking: mapping a URL or an app’s own scheme to a specific screen. The problem is that naive deep links are both fragile (the OS can’t verify who owns the scheme) and shallow (they dump users at your landing page). The solution in 2026 is App Links on Android and Universal Links on iOS — verified, https-based links that route users straight to the right destination.

In this tutorial, you will learn what deep links are, how Android App Links and iOS Universal Links work, and how to implement them in a Flutter app. Key technologies: intent-filter verification, the Digital Asset Links file, Associated Domains, and Flutter’s routing layer.

Prerequisites

  • A published web domain you control (HTTPS required)
  • An Android app with a main activity that handles intents
  • Basic understanding of Android manifest and iOS entitlements

Core Content

There are two flavors of deep linking:

  1. Custom scheme deep links (myapp://profile/42) — simple but unverifiable. Any app can declare myapp://, so the OS may prompt the user to choose which app opens it. Also blocked or unreliable from many web contexts.
  2. App Links (https://example.com/profile/42) — real HTTPS URLs. The OS fetches a Digital Asset Links file from your domain, cryptographically verifies your app owns both the domain and the intent handler, and opens your app without a chooser dialog.

Android App Links are the secure, modern approach and are the recommended replacement for custom schemes.

Declare the intent filter on Android

Add an intent filter to your app’s launcher activity in AndroidManifest.xml:

<activity android:name=".MainActivity">
    <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="example.com"
            android:pathPrefix="/profile" />
    </intent-filter>
</activity>

The android:autoVerify="true" attribute is critical — it tells Android to verify this app against the domain during installation.

For verification to succeed, your domain must serve a JSON file at https://example.com/.well-known/assetlinks.json listing your app’s signing certificate fingerprint and package name:

[
  {
    "relation": ["delegate_permission/common.handle_all_urls"],
    "target": {
      "namespace": "android_app",
      "package_name": "com.example.myapp",
      "sha256_cert_fingerprints": [
        "14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:AD:A3:48:B1:A6:14:1C:2D:44:4E:B4:DA:53:38:3C:7E"
      ]
    }
  }
]

You can obtain the SHA-256 fingerprint from your signing key (keytool -list -v for the release keystore, or the Play Console). During development, use the debug keystore’s fingerprint.

Android Studio’s App Links Assistant (Tools → App Links Assistant) walks through all four steps — adding the intent filter, mapping URLs to activities, generating the Digital Asset Links file, and testing with adb:

adb shell am start -W -a android.intent.action.VIEW -d "https://example.com/profile/42"

If verification succeeded, the link opens your app directly. You can check the verification status on the device with:

adb shell pm verify-app-links com.example.myapp

Universal Links are the iOS equivalent. Add the com.apple.developer.associated-domains entitlement with the applinks service, then serve an apple-app-site-association file (no .well-known prefix; it must be at the domain root) with the AASA content type:

https://example.com/apple-app-site-association
{
  "applinks": {
    "details": [
      {
        "appIDs": ["ABCDE12345.com.example.myapp"],
        "components": [
          { "/": "/profile/*", "comment": "Matches /profile/42" }
        ]
      }
    ]
  }
}

The file must be served over HTTPS with Content-Type: application/json (or application/pkcs7-mime for signed files).

With the platform side configured, use Flutter’s router to turn the incoming URL into a destination. With go_router, parse the path and navigate:

final router = GoRouter(
  routes: [
    GoRoute(
      path: '/',
      builder: (context, state) => const HomeScreen(),
    ),
    GoRoute(
      path: '/profile/:id',
      builder: (context, state) => ProfileScreen(
        userId: state.pathParameters['id']!,
      ),
    ),
  ],
);

The flutter_deep_linking or app_links packages listen for the initial and subsequent links and push them into the router. Test with:

flutter run
# From another terminal:
adb shell am start -a android.intent.action.VIEW -d "https://example.com/profile/42"

Putting It All Together

A working setup has three coordinated pieces: the platform declaration (intent filter with autoVerify on Android, Associated Domains on iOS), the hosted verification files (assetlinks.json / apple-app-site-association), and a router that maps verified URLs to screens. Tools like flutter_deep_linking and the DevTools deep-links validator help you confirm each part before shipping.

Conclusion & Next Steps

You’ve implemented verified, secure deep linking with Android App Links and iOS Universal Links, published the association files, and wired the URLs into a Flutter router. Users now land on the exact screen they tapped.

Next Steps: add error handling for stale links, track deep-link conversions in analytics, and consider deferred deep linking — opening the app store for new users first, then routing them once the app installs.

References: