Explore deep linking in Flutter to seamlessly navigate users to specific content within your app. Learn how to implement and handle deep links efficiently. Deep Linking in Flutter is a powerful technique that allows developers to direct users to specific content or pages within a Flutter application from an external source, such as a URL, push notification, or another app. Deep linking enhances the user experience by providing a seamless transition to the desired content, bypassing the need to navigate through the app manually. In Flutter, deep linking can be implemented using different approaches, including URL-based deep linking, Uni links, or App Links. These methods allow the app to recognize specific patterns in URLs and navigate to the corresponding page or screen.
Create flutter project
flutter created deeplink
open your project pubspec.yaml and add the following package
uni_links: ^0.5.1
add internet permission in your AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
add the following code AndroidManifest.xml under <activity> tags
<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="unlinks.codericu.com"
android:path="/items" />
<data
android:scheme="https"
android:host="unlinks.codericu.com"
android:path="/profile" />
</intent-filter>
replace your subdomain with unlinks.codericu.com
routes.dart in your lib folder
class AppRoutes {
static const String SplashScreen = '/splash_screen';
static const String HomeScreen = '/home';
static const String items = '/items';
static const String checkitems = '/checkitems';
static const String userprofile = '/profile';
}
home.dart in your lib folder
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/container.dart';
import 'package:flutter/src/widgets/framework.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(child: Text("home")),
);
}
}
create splashscreen.dart in your lib folder
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/container.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'routes.dart';
class SplashScreen extends StatefulWidget {
const SplashScreen({super.key});
@override
State<SplashScreen> createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
@override
void initState() {
// TODO: implement initState
super.initState();
Timer(
const Duration(seconds: 3),
() => Navigator.pushNamed(context, AppRoutes.HomeScreen),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(child: Text("Splash Screen")),
);
}
}
checkcategory.dart in your lib folder
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/container.dart';
import 'package:flutter/src/widgets/framework.dart';
class CheckCategory extends StatefulWidget {
final String? id;
const CheckCategory({super.key, this.id});
@override
State<CheckCategory> createState() => _CheckCategoryState();
}
class _CheckCategoryState extends State<CheckCategory> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(child: Text('ID ${widget.id}')),
);
}
}
userprofile.dart in lib folder
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/container.dart';
import 'package:flutter/src/widgets/framework.dart';
class Userprofile extends StatefulWidget {
final String? profileid;
const Userprofile({super.key, required this.profileid});
@override
State<Userprofile> createState() => _UserprofileState();
}
class _UserprofileState extends State<Userprofile> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(child: Text('profile Id : ${widget.profileid}')),
);
}
}
replace main.dart with
import 'package:deeplink/home.dart';
import 'package:deeplink/routes.dart';
import 'package:deeplink/splashscreen.dart';
import 'package:deeplink/userprofile.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:uni_links/uni_links.dart';
import 'checkcategory.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
Uri? uri;
String? deepLinkId;
String? itemId;
bool isUniLinksInitialized = false;
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
Future<void> initUniLinks() async {
debugPrint('Setting up deep link subscription');
debugPrint('deep link init $isUniLinksInitialized');
try {
final initialUri = await getInitialUri();
if (initialUri != null) {
debugPrint('Initial deep link: $initialUri');
handleDeepLink(initialUri);
}
getUriLinksStream().listen((Uri? uri) {
if (uri != null) {
debugPrint('Deep link: $uri');
handleDeepLink(uri);
}
}, onError: (err) {
debugPrint('Error receiving deep link: $err');
});
} on PlatformException {
debugPrint('Error initializing deep link');
}
}
void handleDeepLink(Uri uri) {
if (uri.path == '/items' && uri.queryParameters['id'] != null) {
itemId = uri.queryParameters['id']!;
debugPrint('Navigating to item with deep ID: $itemId');
// Check if SharedItemDetails is already on the stack
final currentRoute = ModalRoute.of(navigatorKey.currentContext!);
if (currentRoute?.settings.name != AppRoutes.checkitems) {
// Use pushReplacementNamed to replace the current route
navigatorKey.currentState?.pushReplacementNamed(
AppRoutes.checkitems,
arguments: CheckCategory(id: itemId),
);
}
} else if (uri.path == '/profile' && uri.queryParameters['id'] != null) {
itemId = uri.queryParameters['id']!;
debugPrint('Navigating to profile with ID: $itemId');
// Check if Userprofile is already on the stack
final currentRoute = ModalRoute.of(navigatorKey.currentContext!);
if (currentRoute?.settings.name != AppRoutes.userprofile) {
// Use pushReplacementNamed to replace the current route
navigatorKey.currentState?.pushReplacementNamed(
AppRoutes.userprofile,
arguments: Userprofile(profileid: itemId),
);
}
}
}
@override
void initState() {
super.initState();
initUniLinks();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
navigatorKey: navigatorKey,
debugShowCheckedModeBanner: false,
initialRoute: determineInitialRoute(),
routes: {
AppRoutes.SplashScreen: (context) => SplashScreen(),
AppRoutes.HomeScreen: (context) => const HomeScreen(),
AppRoutes.checkitems: (context) => CheckCategory(
id: itemId,
),
AppRoutes.userprofile: (context) => Userprofile(
profileid: itemId,
)
},
onGenerateRoute: (settings) {
if (settings.name == AppRoutes.checkitems) {
return MaterialPageRoute(
builder: (context) => CheckCategory(id: itemId),
);
}
return null;
},
title: "XYZ",
);
}
String determineInitialRoute() {
if (itemId != null) {
debugPrint(' deep link id $itemId');
return AppRoutes.checkitems;
} else {
debugPrint('null deep link id $itemId');
return AppRoutes.SplashScreen;
}
}
}
at the end lets setup our server side
first create .well-known folder in your hosting root directory
will show dim like above screenshot
now create 2 files under .well-known folder
- apple-app-site-association
- assetlinks.json
apple-app-site-association
{
"applinks": {
"apps": [],
"details": [
{
"appID": "ZYPW69XW47.com.example.deeplink",
"paths": [ "*" ]
}
]
}
}
replace your ios app id with bundle id
assetlinks.json
[
{
"relation": [
"delegate_permission/common.handle_all_urls"
],
"target": {
"namespace": "android_app",
"package_name": "com.google.android.gm",
"sha256_cert_fingerprints": [
"B1:3E:D4:13:2C:D0:69:A9:94:19:F9:73:7A:F9:C7:79:05:5C:C3:0D:2D:C6:38:B8:5D:29:D6:46:3C:95:23:F0"
]
}
}
]
replace package_name with your android app package name
now create sub domain with any name what every you want i have created with unlink it will auto create folder in your root directory
now create 2 folders under unlink
open items folder
create 2 php files for redirection
index.php
<?php
$userAgent = $_SERVER['HTTP_USER_AGENT'];
$deepLink = isset($_GET['id']) ? $_GET['id'] : null;
// Map different device types to corresponding app URLs
$deviceMappings = [
// replace your android and ios links here
'Android' => 'https://play.google.com/store/apps/details?id=com.google.android.gm',
'iPhone' => 'https://google.com',
// Add more mappings as needed
];
// Determine the default redirect URL
$defaultRedirectUrl = 'pmp.php'; // Redirect to pmp.php for other devices or unknown
// Determine the device type based on the user agent
$deviceType = 'Other'; // Default to 'Other' if not recognized
foreach ($deviceMappings as $type => $url) {
if (stripos($userAgent, $type) !== false) {
$deviceType = $type;
break;
}
}
// Determine the redirect URL based on the device type
$redirectUrl = $deviceMappings[$deviceType] ?? $defaultRedirectUrl;
// Display a message on the page if the device type is not recognized
if ($deviceType === 'Other') {
echo '<html><body>';
echo '<h1>XYZ is not available for web.</h1>';
echo '<p>Please download the app from the ';
echo '<a href="' . $deviceMappings['Android'] . '"><img src="android_icon.png" alt="Play Store"></a> or ';
echo '<a href="' . $deviceMappings['iPhone'] . '"><img src="iphone_icon.png" alt="App Store"></a></p>';
echo '</body></html>';
exit;
}
// Redirect to the determined URL
header("Location: $redirectUrl");
exit;
?>
pmp.php
<?php
//replace your ios, android links here
$deviceMappings = [
'Android' => 'https://play.google.com/store/apps/details?id=com.google.android.gm',
'iPhone' => 'https://google.com',
// Add more mappings as needed
];
// Improved HTML design
echo '<html lang="en"><head>';
echo '<meta charset="UTF-8">';
echo '<meta name="viewport" content="width=device-width, initial-scale=1.0">';
echo '<style>';
echo 'body { font-family: Arial, sans-serif; background-color: #f4f4f4; text-align: center; margin: 0; }';
echo 'h1 { color: #333; margin-top: 50px; }';
echo 'p { color: #555; }';
echo 'a { text-decoration: none; }';
echo 'img { width: 150px; margin: 20px; }';
echo '</style>';
echo '</head><body>';
echo '<h1>XYZ is not available for web.</h1>';
echo '<p>Please download the app from the ';
echo '<a href="' . $deviceMappings['Android'] . '" target="_blank"><img src="android_icon.png" alt="Play Store"></a> or ';
echo '<a href="' . $deviceMappings['iPhone'] . '" target="_blank"><img src="iphone_icon.png" alt="App Store"></a></p>';
echo '</body></html>';
exit;
?>
?>
now open profile folder above we created and create 2 files the same in profile folder
profile >> index.php
<?php
$userAgent = $_SERVER['HTTP_USER_AGENT'];
$deepLink = isset($_GET['id']) ? $_GET['id'] : null;
// Map different device types to corresponding app URLs
$deviceMappings = [
//replace your ios , android links here
'Android' => 'https://play.google.com/store/apps/details?id=com.google.android.gm',
'iPhone' => 'https://google.com',
// Add more mappings as needed
];
// Determine the default redirect URL
$defaultRedirectUrl = 'pmp.php'; // Redirect to pmp.php for other devices or unknown
// Determine the device type based on the user agent
$deviceType = 'Other'; // Default to 'Other' if not recognized
foreach ($deviceMappings as $type => $url) {
if (stripos($userAgent, $type) !== false) {
$deviceType = $type;
break;
}
}
// Determine the redirect URL based on the device type
$redirectUrl = $deviceMappings[$deviceType] ?? $defaultRedirectUrl;
// Display a message on the page if the device type is not recognized
if ($deviceType === 'Other') {
echo '<html><body>';
echo '<h1>XYZ is not available for web.</h1>';
echo '<p>Please download the app from the ';
echo '<a href="' . $deviceMappings['Android'] . '"><img src="android_icon.png" alt="Play Store"></a> or ';
echo '<a href="' . $deviceMappings['iPhone'] . '"><img src="iphone_icon.png" alt="App Store"></a></p>';
echo '</body></html>';
exit;
}
// Redirect to the determined URL
header("Location: $redirectUrl");
exit;
?>
profile >> pmp.php
<?php
$deviceMappings = [
//replace your ios,android links here
'Android' => 'https://play.google.com/store/apps/details?id=com.google.android.gm',
'iPhone' => 'https://google.com',
// Add more mappings as needed
];
// Improved HTML design
echo '<html lang="en"><head>';
echo '<meta charset="UTF-8">';
echo '<meta name="viewport" content="width=device-width, initial-scale=1.0">';
echo '<style>';
echo 'body { font-family: Arial, sans-serif; background-color: #f4f4f4; text-align: center; margin: 0; }';
echo 'h1 { color: #333; margin-top: 50px; }';
echo 'p { color: #555; }';
echo 'a { text-decoration: none; }';
echo 'img { width: 150px; margin: 20px; }';
echo '</style>';
echo '</head><body>';
echo '<h1>XYZ is not available for web.</h1>';
echo '<p>Please download the app from the ';
echo '<a href="' . $deviceMappings['Android'] . '" target="_blank"><img src="android_icon.png" alt="Play Store"></a> or ';
echo '<a href="' . $deviceMappings['iPhone'] . '" target="_blank"><img src="iphone_icon.png" alt="App Store"></a></p>';
echo '</body></html>';
exit;
?>
?>
Demo Apk : https://tii.la/ihWOUKpI
Source Code : https://tii.la/HWGaFfcptC