Universal Links And App Links Setup Guide With Common Gotchas
Jan 27, 2026



Summary
Summary
Summary
Summary
This guide explains Universal Links (iOS) and App Links (Android) for flutter mobile development: create and host apple-app-site-association and assetlinks.json correctly, configure entitlements/intent-filters, verify with curl/adb, and capture links in Flutter with uni_links. Common failures are redirects, mismatched IDs/fingerprints, using debug keys, and caching issues.
This guide explains Universal Links (iOS) and App Links (Android) for flutter mobile development: create and host apple-app-site-association and assetlinks.json correctly, configure entitlements/intent-filters, verify with curl/adb, and capture links in Flutter with uni_links. Common failures are redirects, mismatched IDs/fingerprints, using debug keys, and caching issues.
This guide explains Universal Links (iOS) and App Links (Android) for flutter mobile development: create and host apple-app-site-association and assetlinks.json correctly, configure entitlements/intent-filters, verify with curl/adb, and capture links in Flutter with uni_links. Common failures are redirects, mismatched IDs/fingerprints, using debug keys, and caching issues.
This guide explains Universal Links (iOS) and App Links (Android) for flutter mobile development: create and host apple-app-site-association and assetlinks.json correctly, configure entitlements/intent-filters, verify with curl/adb, and capture links in Flutter with uni_links. Common failures are redirects, mismatched IDs/fingerprints, using debug keys, and caching issues.
Key insights:
Key insights:
Key insights:
Key insights:
Understanding Universal Links And App Links: Domain files bind HTTPS URLs to apps—serve them over HTTPS without redirects and match identifiers exactly.
iOS Setup: Use an apple-app-site-association file, enable Associated Domains with applinks:your.domain, and ensure team ID and bundle ID match.
Android Setup: Host .well-known/assetlinks.json with package name and release SHA256 fingerprint and add intent-filter with android:autoVerify="true".
Flutter Integration: Use uni_links or Firebase Dynamic Links to handle getInitialUri() and uriLinkStream for cold and runtime linking.
Testing And Common Gotchas: Verify with curl and adb, avoid redirects, use release signing fingerprints (not debug), and remember iOS AASA caching.
Introduction
Universal Links (iOS) and App Links (Android) let https:// links open your mobile app instead of the browser. For flutter mobile development, these mechanisms are essential to provide seamless link-to-content experiences, support deep linking from emails, social shares, and push notifications, and improve conversion flows. This guide focuses on precise setup steps, verification commands, Flutter integration, and the common gotchas that commonly break deployments.
Understanding Universal Links And App Links
Conceptually both technologies bind a domain to an installed app so that HTTP(S) URLs targeted at that domain can deep-link directly into the app. Key components:
Domain file: iOS uses apple-app-site-association (AASA), Android uses .well-known/assetlinks.json.
App configuration: iOS requires Associated Domains entitlement, Android requires intent filters with autoVerify and package/sha256 settings.
HTTPS hosting: files must be served over HTTPS without redirects, with correct content-type.
Keep these rules in mind: the domain file must exactly match package/team IDs and certificate fingerprints. Any mismatch or redirect will cause verification to fail and links will open in the browser instead of the app.
iOS Setup
1) Create an AASA file (no .json extension required) for each domain and place it at /.well-known/apple-app-site-association or at the domain root. Minimal example:
{
"applinks": {
"details": [
{ "appID": "TEAMID.com.example.app", "paths": [ "/path/*", "/" ] }
]
}
}
2) Host this file on HTTPS with no redirects. Content-Type should be application/json (or application/pkcs7-signature if signed).
3) In Xcode, enable Associated Domains capability, and add: applinks:your.domain.com
4) Verify: on device, open the domain URL in Safari, then check device logs (Console.app when device connected) for AASA download and any errors. iOS caches AASA aggressively—remove and reinstall the app after fixing the file or change the bundle identifier.
Common iOS gotchas: wrong team ID in appID, missing applinks prefix in entitlements, redirect from domain to CDN or to www (ensure the exact URL path where the file is hosted), and using HTTP during testing.
Android Setup
1) Create an assetlinks.json hosted at https://your.domain.com/.well-known/assetlinks.json. Example entry:
[
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.example.app",
"sha256_cert_fingerprints": ["AB:CD:...:12"]
}
}
]
2) Add an intent-filter to AndroidManifest.xml inside the activity that should handle links (use the release package name):
3) Build with the signing key whose SHA256 fingerprint you published in assetlinks.json. For Play signing, use the app signing key from Play Console.
Verify: use curl to fetch the assetlinks.json and run: adb shell pm get-app-links com.example.app (or adb shell pm verify-app-links --re-verify com.example.app).
Common Android gotchas: using debug keystore fingerprint instead of release, hosting file behind redirects, typos in package name, missing autoVerify, and confusing Play App Signing fingerprints.
Testing And Common Gotchas
Checklist for both platforms:
Fetch files with curl: curl -I https://your.domain.com/.well-known/assetlinks.json and curl -I https://your.domain.com/.well-known/apple-app-site-association. Expect 200 and content-type application/json.
Ensure no 301/302 redirects. Redirects break verification.
Match values exactly: team ID, bundle ID, package name, SHA256 fingerprint.
For Android, remember the fingerprint must be the signing certificate used in production (not debug). For apps distributed via Play, use Play Console’s app signing certificate.
iOS caches the AASA. Reinstall the app and reboot device when troubleshooting.
Browser behavior: some browsers open in-app after initial Safari handshake; testing in Chrome on Android can show different behavior than a link tapped from an email client.
Flutter integration tips
Use a link handler plugin like uni_links or firebase_dynamic_links to capture link events in Flutter. Handle both initial (cold start) and stream (runtime) links. Example with uni_links:
import 'dart:async'; import 'package:uni_links/uni_links.dart'; StreamSubscription? _sub; void initLinks(void Function(Uri) handleUri) { _sub = uriLinkStream.listen((Uri? uri) { if (uri != null) handleUri(uri); }); } Future<void> checkInitial(void Function(Uri) handleUri) async { final initial = await getInitialUri(); if (initial != null) handleUri(initial); }
Remember to dispose subscriptions and to test cold-start navigation (user taps link when app is not running) separately from when the app is in foreground.
Vibe Studio

Vibe Studio, powered by Steve’s advanced AI agents, is a revolutionary no-code, conversational platform that empowers users to quickly and efficiently create full-stack Flutter applications integrated seamlessly with Firebase backend services. Ideal for solo founders, startups, and agile engineering teams, Vibe Studio allows users to visually manage and deploy Flutter apps, greatly accelerating the development process. The intuitive conversational interface simplifies complex development tasks, making app creation accessible even for non-coders.
Conclusion
Universal Links and App Links are powerful but strict: domain files, exact identifiers, HTTPS, and correct signing must all match. For flutter mobile development, the bulk of work is outside Dart—configure iOS/Android correctly, host JSON files properly, then use uni_links or Firebase to route URIs inside your app. If links open in a browser, re-check redirects, fingerprints, and entitlements first—those are the usual culprits.
Introduction
Universal Links (iOS) and App Links (Android) let https:// links open your mobile app instead of the browser. For flutter mobile development, these mechanisms are essential to provide seamless link-to-content experiences, support deep linking from emails, social shares, and push notifications, and improve conversion flows. This guide focuses on precise setup steps, verification commands, Flutter integration, and the common gotchas that commonly break deployments.
Understanding Universal Links And App Links
Conceptually both technologies bind a domain to an installed app so that HTTP(S) URLs targeted at that domain can deep-link directly into the app. Key components:
Domain file: iOS uses apple-app-site-association (AASA), Android uses .well-known/assetlinks.json.
App configuration: iOS requires Associated Domains entitlement, Android requires intent filters with autoVerify and package/sha256 settings.
HTTPS hosting: files must be served over HTTPS without redirects, with correct content-type.
Keep these rules in mind: the domain file must exactly match package/team IDs and certificate fingerprints. Any mismatch or redirect will cause verification to fail and links will open in the browser instead of the app.
iOS Setup
1) Create an AASA file (no .json extension required) for each domain and place it at /.well-known/apple-app-site-association or at the domain root. Minimal example:
{
"applinks": {
"details": [
{ "appID": "TEAMID.com.example.app", "paths": [ "/path/*", "/" ] }
]
}
}
2) Host this file on HTTPS with no redirects. Content-Type should be application/json (or application/pkcs7-signature if signed).
3) In Xcode, enable Associated Domains capability, and add: applinks:your.domain.com
4) Verify: on device, open the domain URL in Safari, then check device logs (Console.app when device connected) for AASA download and any errors. iOS caches AASA aggressively—remove and reinstall the app after fixing the file or change the bundle identifier.
Common iOS gotchas: wrong team ID in appID, missing applinks prefix in entitlements, redirect from domain to CDN or to www (ensure the exact URL path where the file is hosted), and using HTTP during testing.
Android Setup
1) Create an assetlinks.json hosted at https://your.domain.com/.well-known/assetlinks.json. Example entry:
[
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.example.app",
"sha256_cert_fingerprints": ["AB:CD:...:12"]
}
}
]
2) Add an intent-filter to AndroidManifest.xml inside the activity that should handle links (use the release package name):
3) Build with the signing key whose SHA256 fingerprint you published in assetlinks.json. For Play signing, use the app signing key from Play Console.
Verify: use curl to fetch the assetlinks.json and run: adb shell pm get-app-links com.example.app (or adb shell pm verify-app-links --re-verify com.example.app).
Common Android gotchas: using debug keystore fingerprint instead of release, hosting file behind redirects, typos in package name, missing autoVerify, and confusing Play App Signing fingerprints.
Testing And Common Gotchas
Checklist for both platforms:
Fetch files with curl: curl -I https://your.domain.com/.well-known/assetlinks.json and curl -I https://your.domain.com/.well-known/apple-app-site-association. Expect 200 and content-type application/json.
Ensure no 301/302 redirects. Redirects break verification.
Match values exactly: team ID, bundle ID, package name, SHA256 fingerprint.
For Android, remember the fingerprint must be the signing certificate used in production (not debug). For apps distributed via Play, use Play Console’s app signing certificate.
iOS caches the AASA. Reinstall the app and reboot device when troubleshooting.
Browser behavior: some browsers open in-app after initial Safari handshake; testing in Chrome on Android can show different behavior than a link tapped from an email client.
Flutter integration tips
Use a link handler plugin like uni_links or firebase_dynamic_links to capture link events in Flutter. Handle both initial (cold start) and stream (runtime) links. Example with uni_links:
import 'dart:async'; import 'package:uni_links/uni_links.dart'; StreamSubscription? _sub; void initLinks(void Function(Uri) handleUri) { _sub = uriLinkStream.listen((Uri? uri) { if (uri != null) handleUri(uri); }); } Future<void> checkInitial(void Function(Uri) handleUri) async { final initial = await getInitialUri(); if (initial != null) handleUri(initial); }
Remember to dispose subscriptions and to test cold-start navigation (user taps link when app is not running) separately from when the app is in foreground.
Vibe Studio

Vibe Studio, powered by Steve’s advanced AI agents, is a revolutionary no-code, conversational platform that empowers users to quickly and efficiently create full-stack Flutter applications integrated seamlessly with Firebase backend services. Ideal for solo founders, startups, and agile engineering teams, Vibe Studio allows users to visually manage and deploy Flutter apps, greatly accelerating the development process. The intuitive conversational interface simplifies complex development tasks, making app creation accessible even for non-coders.
Conclusion
Universal Links and App Links are powerful but strict: domain files, exact identifiers, HTTPS, and correct signing must all match. For flutter mobile development, the bulk of work is outside Dart—configure iOS/Android correctly, host JSON files properly, then use uni_links or Firebase to route URIs inside your app. If links open in a browser, re-check redirects, fingerprints, and entitlements first—those are the usual culprits.
Build Flutter Apps Faster with Vibe Studio
Vibe Studio is your AI-powered Flutter development companion. Skip boilerplate, build in real-time, and deploy without hassle. Start creating apps at lightning speed with zero setup.
Other Insights






















