Flutter Deeplinking without using Firebase (Android)

Walnut Software
5 min readNov 17, 2023

In this article, I will explain how to create Deeplinks in Flutter and how to open your application using these Deeplinks. With the help of this article, you will be able to open both your application and the desired page using Deeplinks. You will also be able to redirect to the desired page with Deeplinks while your application is already open.

Setting Up the Project

Open a new terminal. Navigate to the directory where you want to create a new Flutter project using terminal commands. Then, use the following command to create a new Flutter project:

flutter create example_project

You can replace “example_project” with the name of the project you want to create. In my case project name is deeplinking_example.

Create a Deeplink

● Step 1 Add necessary dependencies:

To define dependencies, first open the pubspec.yaml file, which is located at the bottom of your project. Afterward, add the uni_links package under the "dependencies" section. You can find the latest uni_links package at this link.

https://pub.dev/packages/uni_links/install

The “dependencies” section should look like the following:

dependencies:
flutter:
sdk: flutter
uni_links: ^version

Replace the ‘^version’ with a uni_linkspackage version compatible with your Flutter version. In my case it’s ^0.5.1 , my flutter version: 3.13.2.

● Step 2: Add the following permissions to the AndroidManifest.xml file. You can refer to the images below for guidance on where to add these permissions.

<uses-permission android:name="android.permission.INTERNET" />
<meta-data android:name="flutter_deeplinking_enabled" android:value="true" />
<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:pathPrefix="/data/"
android:host="your_domain_name.com" />
</intent-filter>

If the Deeplink you want to open your application with does not have a specific prefix (in my case it’s /data/ ), you can delete the line android:pathPrefix=”/data/”.

● Step 3: To be able to open your application using Deeplinks, you will need your applicationId and sha-256 key. First, to obtain the sha-256 key, paste and run the following command in the terminal.

For Windows

keytool -list -v -keystore "%USERPROFILE%\.android\debug.keystore" -alias androiddebugkey -storepass android -keypass android

For Mac

keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android | grep "SHA256"

Copy the SHA256 key and store it in a notepad. Now, navigate to the android/app directory in the root of your Flutter project. Open the build.gradle file located in the app folder, copy your applicationId from it, and also add it to your notepad.

● Step 4: Go to your hosting provider’s file manager and create a “.well-known” folder in the root directory where your website is located. Inside this folder, add the “assetlinks.json” file that you’ve organized as shown below.

📌 Attention: If you don’t have a hosting provider, you can get a free hosting site domain from the following link.

https://000webhost.com

[{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.your_application_name",
"sha256_cert_fingerprints":
["your_sha256_key"]
}
}]

We will use the applicationId and sha256 key that I previously asked you to save in your notepad. Replace “com.your_application_name” with your applicationId and “your_sha256_key” with your sha256 key. After making these changes, save the file and then add it to the “.well-known” folder.

● Step 5: Before using your Deeplink in your application, you can test it by using the link below. If you’ve done everything correctly, you should receive the “Success!” message as shown in the image below.

https://developers.google.com/digital-asset-links/tools/generator

Deeplink Usage

The code below enables you to open your application using deep links and allows your application to listen for deep links while running in the background. You can add this code to your main.dart file and use it.

You know you can perform contextless Navigation. To perform page navigation using deep links, add theget package to your application through the link below, uncomment the lines I've interpreted, and make sure to replace 'return MaterialApp' with 'return GetMaterialApp'. This will allow you to perform page navigation without needing to use context directly, offering a contextless way of navigating between pages in your application.

https://pub.dev/packages/get/install

import 'dart:async';
import 'package:your_project_name/pages/login_screen.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:uni_links/uni_links.dart';
//import 'package:get/get.dart';

bool _initialUriIsHandled = false;
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp>{
Uri? _initialUri;
Uri? _latestUri;
Object? _err;

StreamSubscription? _sub;

@override
void initState() {
super.initState();
_handleIncomingLinks();
_handleInitialUri();
}

@override
void dispose() {
_sub?.cancel();
super.dispose();
}

void _handleIncomingLinks() {
if (!kIsWeb) {
_sub = uriLinkStream.listen((Uri? uri) {
if (!mounted) return;
print('got uri: $uri');
//Get.to(()=> RegisterScreen());
setState(() {
_latestUri = uri;
_err = null;
});
}, onError: (Object err) {
if (!mounted) return;
print('got err: $err');
setState(() {
_latestUri = null;
if (err is FormatException) {
_err = err;
} else {
_err = null;
}
});
});
}
}
Future<void> _handleInitialUri() async {
if (!_initialUriIsHandled) {
_initialUriIsHandled = true;
try {
final uri = await getInitialUri();
if (uri == null) {
print('no initial uri');
} else {
print('got initial uri: $uri');
//Get.to(()=> RegisterScreen());
}
if (!mounted) return;
setState(() => _initialUri = uri);
} on PlatformException {
print('falied to get initial uri');
} on FormatException catch (err) {
if (!mounted) return;
print('malformed initial uri');
setState(() => _err = err);
}
}
}
@override
Widget build(BuildContext context) {
//return GetMaterialApp(
return MaterialApp(
theme: ThemeData(
elevatedButtonTheme: ElevatedButtonThemeData(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all<Color>(Colors.red),
),
),
appBarTheme: AppBarTheme(color: Colors.black),
primaryColor: Colors.black,
scaffoldBackgroundColor: Colors.white,
),
debugShowCheckedModeBanner: false,
home: LoginScreen(),
);
//);
}
}

The “_handleInitialUri” function will run when the application is opened with a Deeplink and handle the Deeplink. The “_handleIncomingLinks” function, on the other hand, will run when a Deeplink is clicked while the application is open in the background and handle the Deeplink.

Good Coding!

--

--