How To Do Deeplink in Flutter

Oemam Pedia
4 min readDec 26, 2020

--

If you are looking for how to open your flutter app by particular link, then deeplink may can give you that.

Someday, i have a flutter app, and i wanted to know how can i open my flutter app by url link, so if the user was installing my app before, then i liked to redirect to my flutter app. and if the user wasn’t installing the app, then redirect to browser.

There are bunch ways to do deeplink. first, i use one of the flutter package to handle deeplink, called uni_links. it can work well. but, there is something behavior that i don’t want. what is the problem?, if the app was closed and redirect from url. the deeplink data still keep storing, even i have used it.

Then i look for other ways. i do android native approach, that is android. because, i don’t have any apple devices T_T. After that, I found a nice article, here is the link. it give you the way to do in both of Android and iOS device, so you can do the same thing to iOS.

But, still i facing the same problem with uni_links. because i wrote the code by myself, so i can change the code without opening other people code :D.

Let’s try it.

AndroidManifest

Add some code to your manifest. app/src/main/AndroidManifest.xml

<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="http"
android:host="mydomain.com"/>
</intent-filter>

then, change android:launchMode to singleTask in your AndroidManifest, android:launchMode=”singleTask” . so may look like this

It will prevent the app to opening two or more of your app running in the background. So, if your app was is in the background. then, it will be resumed your app instead of creating new proccess.

Implements Platform Channel

let’s create a native code, so that later we can call it into dart code.

open your MainActivity and add the code bellow.

class MainActivity: FlutterActivity() {

private val CHANNEL = "channel"
private val EVENTS = "events"
private var startString: String? = null
private var
linksReceiver: BroadcastReceiver? = null

override fun
configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine)

MethodChannel(flutterEngine.dartExecutor, CHANNEL).setMethodCallHandler { call, result ->
if (call.method == "initialLink") {
if (startString != null) {
val res = startString
startString = null
result.success(res)
} else {
result.error("notIntent", "link is null", null)
}
}
}

EventChannel(flutterEngine.dartExecutor, EVENTS).setStreamHandler(
object : EventChannel.StreamHandler {
override fun onListen(args: Any?, events: EventChannel.EventSink) {
linksReceiver = createChangeReceiver(events)
}

override fun onCancel(args: Any?) {
linksReceiver = null
}
}
)
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

val intent = getIntent()
startString = intent.data?.toString()
}

override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
if (intent.action === Intent.ACTION_VIEW) {
linksReceiver?.onReceive(this.applicationContext, intent)
}
}

fun createChangeReceiver(events: EventChannel.EventSink): BroadcastReceiver? {
return object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) { // NOTE: assuming intent.getAction() is Intent.ACTION_VIEW
val
dataString = intent.dataString ?:
events.error("UNAVAILABLE", "Link unavailable", null)
events.success(dataString)
}
}
}
}

There is MethodChannel and EventChannel. What can they do?

MethodChannel : it will be called if the app was launch from the start.

EventChannel : we use to stream data, if the app was launch before. so we can listen the data and do onResume to our flutter app.

Call in The Dart Side

let’s take a look how we implement it into your dart code.

add the code below to your screen.

import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class MyHomeApp extends StatefulWidget {
@override
_MyHomeAppState createState() => new _MyHomeAppState();
}

class _MyHomeAppState extends State<MyHomeApp> {

final stream = const EventChannel('events');
StreamController<String> _stateController = StreamController();
//Method channel creation
static const platform
= const MethodChannel('channel');

@override
initState() {
super.initState();
//Checking application start by deep link
startUri().then(_onRedirected).catchError((e) => print("unavailable link"));
//Checking broadcast stream, if deep link was clicked in opened appication
stream.receiveBroadcastStream().listen((d) => _onRedirected(d));
}

_onRedirected(String uri) {
// Here can be any uri analysis, checking tokens etc, if it’s necessary
// Throw deep link URI into the BloC's stream
print("URI LAUNCH : $uri");
final links = Uri.parse(uri);
print("URI LAUNCH PATH : ${links.path}");
print("URI LAUNCH PATH SEGMENTS : ${links.pathSegments}");
print("URI LAUNCH QUERY : ${links.queryParameters}");

// you can redirect to another page by listen the path segments
// EG: product/shoes -> then i want to redirect to product screen

_stateController.sink.add(uri); // ignore this if you wanted to redirect to another page
}

Future<String> startUri() async {
try {
return platform.invokeMethod('initialLink');
} on PlatformException catch (e) {
throw(e);
}
}

@override
dispose() {
if (_stateController != null) _stateController.close();
super.dispose();
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Plugin example app'),
),
body: StreamBuilder<String>(
stream: _stateController.stream,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Container(
child: Center(
child: Text('No deep link was used ')));
} else {
final links = Uri.parse(snapshot.data);
return Container(
child: Center(
child: Padding(
padding: EdgeInsets.all(20.0),
child: Text('Redirected: ${links.path} - ${links.query}'))));
}
},
),
);
}
}

if you want to redirect the app to another page, you can ignore stream builder and stream sink.

i put my example app on the github if you wanna take a look. here

don’t be hesitate to comment below :D.

--

--

Oemam Pedia

Just wanna share and improve my english language by write an article.