Deep linking is a crucial feature for mobile applications, allowing users to navigate directly to specific content within the app from external sources such as web pages, emails, or other apps. In Flutter, handling different types of deep links involves configuring your app to respond to URL schemes (also known as custom schemes), Android App Links, and iOS Universal Links. This guide provides a comprehensive overview of implementing deep linking in Flutter apps to ensure a seamless user experience.
Understanding Deep Links
Before diving into the implementation, let’s understand the different types of deep links:
- URL Schemes (Custom Schemes): These are custom URL prefixes that an app registers to handle. For example,
myapp://content/123
. - Android App Links: Standard HTTP URLs that point to both your website and your app. When a user clicks on an App Link, Android will directly open your app (if installed) without showing the disambiguation dialog.
- iOS Universal Links: Similar to Android App Links, Universal Links use standard HTTP/HTTPS URLs that link to both your website and your app on iOS.
Setting Up URL Schemes in Flutter
URL schemes are the simplest form of deep linking. Here’s how to configure them:
Step 1: Update Native Project Configurations
Android (AndroidManifest.xml
)
Add an intent filter to your AndroidManifest.xml
(located in android/app/src/main/
) inside the <activity>
tag:
<activity
android:name=".MainActivity"
android:launchMode="singleTop">
<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="myapp" android:host="content" />
</intent-filter>
</activity>
In this example:
android:scheme="myapp"
defines the custom scheme.android:host="content"
defines the host.
iOS (Info.plist
)
Add the following to your Info.plist
(located in ios/Runner/
):
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>myapp</string>
</array>
<key>CFBundleURLName</key>
<string>com.example.myapp</string> <!-- Replace with your app's bundle identifier -->
</dict>
</array>
Replace com.example.myapp
with your app’s bundle identifier.
Step 2: Implement Flutter Code to Handle the Deep Link
Use the uni_links
package to listen for incoming deep links.
Add the uni_links
package to your pubspec.yaml
:
dependencies:
flutter:
sdk: flutter
uni_links: ^0.5.1
Now, implement the code to listen for deep links in your Flutter app:
import 'package:flutter/material.dart';
import 'package:uni_links/uni_links.dart';
import 'package:flutter/services.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String? _latestLink = 'Unknown';
@override
void initState() {
super.initState();
_initUniLinks();
}
Future<void> _initUniLinks() async {
try {
final initialLink = await getInitialLink();
if (initialLink != null) {
_latestLink = initialLink;
}
// Attach a listener to the stream
linkStream.listen((String? link) {
setState(() {
_latestLink = link ?? 'Unknown';
});
}, onError: (err) {
setState(() {
_latestLink = 'Failed to get latest link: $err.';
});
});
} on PlatformException {
setState(() {
_latestLink = 'Failed to get initial link.';
});
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Deep Linking in Flutter'),
),
body: Center(
child: Text('Latest deep link: $_latestLink'),
),
),
);
}
}
In this code:
getInitialLink()
fetches the link used to open the app initially.linkStream
listens for subsequent deep links while the app is running.- The UI updates to display the latest deep link.
Setting Up Android App Links
Step 1: Configure AssetLinks File
Create a assetlinks.json
file and place it in the .well-known
directory of your domain (e.g., https://yourdomain.com/.well-known/assetlinks.json
). Replace yourdomain.com
and package name with your actual domain and app’s package name:
[
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.example.myapp",
"sha256_cert_fingerprints": [
"YOUR_SHA256_FINGERPRINT"
]
}
}
]
To get your SHA256 certificate fingerprint, use the following command:
keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey
Replace ~/.android/debug.keystore
with the path to your keystore file if necessary. Also ensure you get the SHA256 from your release keystore when publishing the app.
Step 2: Verify the assetlinks.json
File
Ensure that the assetlinks.json
file is accessible over HTTPS. You can verify this by opening https://yourdomain.com/.well-known/assetlinks.json
in a browser. If the file is not accessible, Android will not verify the App Links.
Step 3: Update AndroidManifest.xml
Add the android:autoVerify="true"
attribute to your intent filter in AndroidManifest.xml
and include the android:host
attribute:
<activity
android:name=".MainActivity"
android:launchMode="singleTop">
<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="yourdomain.com" android:pathPrefix="/content" />
</intent-filter>
</activity>
Here, android:scheme="https"
and android:host="yourdomain.com"
define the App Link parameters. Make sure to include pathPrefix or other attributes to define the exact URLs your app can handle. android:autoVerify=”true” tells the system to verify that the app is approved to handle the URLs
Step 4: Handle the App Link in Flutter
Reuse the uni_links
package to handle the incoming App Links in Flutter, as shown in the URL Schemes setup. The key is that your Flutter code will receive the full HTTPS URL, which you can then parse and use to navigate within your app.
Setting Up iOS Universal Links
Step 1: Configure Associated Domains
In your Xcode project, go to the Signing & Capabilities tab. Add the “Associated Domains” capability, and add an entry for each domain that you want to support:
applinks:yourdomain.com
Ensure that you prefix applinks:
to your domain.
Step 2: Create and Upload apple-app-site-association
File
Create an apple-app-site-association
(AASA) file and place it in the .well-known
directory or root directory of your domain (e.g., https://yourdomain.com/.well-known/apple-app-site-association
or https://yourdomain.com/apple-app-site-association
). The file must be served over HTTPS without redirects and with the application/json
content type. Here’s an example of the AASA file content:
{
"applinks": {
"apps": [],
"details": [
{
"appID": "YOUR_TEAM_ID.com.example.myapp",
"paths": ["/content/*"]
}
]
}
}
Replace YOUR_TEAM_ID
with your team ID and com.example.myapp
with your app’s bundle identifier.
Step 3: Handle the Universal Link in Flutter
Use the uni_links
package to handle incoming Universal Links in Flutter, just as you did with URL schemes and App Links. The same linkStream
will emit the Universal Link URL, which your app can then use to navigate accordingly.
Additional Tips and Best Practices
- Error Handling: Implement robust error handling to gracefully handle cases where deep links are malformed or lead to invalid content.
- Testing: Thoroughly test deep linking on both Android and iOS devices, covering different scenarios, such as the app being in the background or not installed.
- Navigation: Use a consistent navigation pattern in your Flutter app to ensure users are taken to the correct destination when a deep link is opened.
- Deferred Deep Linking: Consider using services like Adjust or Branch for deferred deep linking, which handles the case where the app is not yet installed and navigates the user to the correct content after installation.
Conclusion
Handling different types of deep links in Flutter requires careful configuration of both the native Android and iOS projects, as well as Flutter code for listening to incoming links. By implementing URL schemes, Android App Links, and iOS Universal Links, you can create a seamless and engaging user experience, allowing users to navigate directly to relevant content within your app from various sources. Leverage the uni_links
package for easy handling of deep links in Flutter, and remember to test thoroughly to ensure everything works as expected.