Deep linking is a technique that allows users to navigate directly to a specific section within a mobile application from an external source, such as a website, email, or another app. In Flutter, implementing custom deep link logic enhances user experience and increases engagement by providing a seamless pathway to relevant content. This blog post will guide you through the process of implementing custom deep link logic in your Flutter application.
Understanding Deep Links
Deep links come in two primary forms:
- URL Schemes: Traditional deep links use custom URL schemes (e.g.,
myapp://content/123) registered by your app. - Universal Links (Android App Links & iOS Universal Links): Standard HTTP/HTTPS URLs that point to both your website and a specific location in your app. These are more secure and reliable compared to URL schemes.
Why Use Deep Links in Flutter?
- Improved User Experience: Directs users to specific content without manual navigation.
- Marketing Campaigns: Tracks and attributes user acquisition via specific campaign links.
- App Invitations: Enables easy sharing and referrals.
- Seamless Navigation: Provides context from external sources to the app.
Prerequisites
- Flutter SDK installed and configured.
- Basic knowledge of Flutter development.
- A code editor such as VS Code or Android Studio.
Step 1: Setting Up the Flutter Project
First, create a new Flutter project if you haven’t already:
flutter create deep_linking_example
cd deep_linking_example
Step 2: Add Dependencies
You’ll need the uni_links package for handling deep links. Add it to your pubspec.yaml file:
dependencies:
flutter:
sdk: flutter
uni_links: ^0.5.1 # Use the latest version
Run flutter pub get to install the dependencies.
Step 3: Configure AndroidManifest.xml (Android)
For URL scheme deep links, modify android/app/src/main/AndroidManifest.xml:
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<!-- Deep Link Intent Filter -->
<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>
For Android App Links (Universal Links), set up asset links by creating a assetlinks.json file and linking it to your domain. Additionally, modify the intent filter in AndroidManifest.xml:
<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>
Step 4: Configure Info.plist (iOS)
For URL scheme deep links, modify ios/Runner/Info.plist:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>myapp</string>
</array>
<key>CFBundleURLName</key>
<string>com.example.deeplinkingexample</string>
</dict>
</array>
For iOS Universal Links, configure Associated Domains in Xcode. Enable the Associated Domains capability and add applinks:yourdomain.com to the list.
Step 5: Implementing Deep Link Handling in Flutter
Update your main.dart file to handle incoming deep links:
import 'dart:async';
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 {
String? _latestLink = 'Unknown';
StreamSubscription? _sub;
@override
void initState() {
super.initState();
_initDeepLinkListener();
}
@override
void dispose() {
_sub?.cancel();
super.dispose();
}
Future _initDeepLinkListener() async {
// Platform messages may fail, so we use a try/catch PlatformException.
try {
final initialLink = await getInitialLink();
if (initialLink != null) {
setState(() {
_latestLink = initialLink;
});
_handleDeepLink(initialLink);
}
} on PlatformException {
print('Failed to get initial link.');
}
// Attach a listener to the stream
_sub = linkStream.listen((String? link) {
if (link != null) {
setState(() {
_latestLink = link;
});
_handleDeepLink(link);
}
}, onError: (err) {
print('Error: $err');
});
}
void _handleDeepLink(String link) {
// Parse the link and navigate accordingly
final uri = Uri.parse(link);
if (uri.scheme == 'myapp' || uri.host == 'yourdomain.com') {
final path = uri.pathSegments.join('/');
switch (path) {
case 'content':
final contentId = uri.queryParameters['id'];
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => ContentScreen(contentId: contentId),
));
break;
default:
print('Unknown deep link: $link');
}
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Deep Linking Example'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Latest Link: $_latestLinkn'),
],
),
),
),
);
}
}
class ContentScreen extends StatelessWidget {
final String? contentId;
ContentScreen({this.contentId});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Content Details'),
),
body: Center(
child: Text('Content ID: ${contentId ?? 'N/A'}'),
),
);
}
}
In this code:
- We initialize the deep link listener in
initState. - The
_initDeepLinkListenermethod retrieves the initial link (if any) and starts listening for incoming links. - The
_handleDeepLinkmethod parses the link and navigates to the appropriate screen. - A
ContentScreenis created to display the content details based on thecontentId.
Step 6: Testing Deep Links
You can test deep links using the following commands:
For URL schemes:
# Android
adb shell am start -W -a android.intent.action.VIEW -d "myapp://content?id=123" com.example.deeplinkingexample
# iOS
xcrun simctl openurl booted "myapp://content?id=123"
For Universal Links (Android App Links & iOS Universal Links):
# Android
adb shell am start -W -a android.intent.action.VIEW -d "https://yourdomain.com/content?id=123" com.example.deeplinkingexample
# iOS
xcrun simctl openurl booted "https://yourdomain.com/content?id=123"
Best Practices
- Error Handling: Implement robust error handling to manage invalid or malformed deep links.
- Fallback Mechanism: Provide a fallback mechanism to handle cases where deep linking fails (e.g., navigating to a generic home screen).
- Secure Deep Links: For sensitive operations, use secure deep links with encryption or authentication.
- Testing: Thoroughly test deep links on different devices and platforms.
Conclusion
Implementing custom deep link logic in Flutter involves configuring your Android and iOS projects, adding necessary dependencies, and handling incoming links within your application. This enhances user experience and enables seamless navigation to specific content within your app. By following this guide, you can create a robust deep linking solution for your Flutter application.