Halo Devs! Semoga masih semangat ya! Artikel ini adalah langkah awal untuk memulai mengimplementasikan Deep linking (Android App Links) pada aplikasi Android menggunakan framework Flutter. Jika belum paham mengenai Deep linking, silakan baca artikel pembukanya dulu ya :)
Seri ini akan dibagi menjadi beberapa artikel mengingat kita harus menyiapkan beberapa hal sebelum benar-benar bisa mengimplementasikan Deep linking pada aplikasi Android kita:
- [Bagian 1] Flutter Deep Linking — Android App Links: Menyiapkan Routing Aplikasi dengan Go Router
- [Bagian 2] Flutter Deep Linking — Android App Links: Menyiapkan Web Server untuk Validasi Kredensial Aplikasi
- [Bagian 3] Flutter Deep Linking — Android App Links: Mengintegrasikan & Menguji Deep Linking
Untuk development, disarankan menggunakan Android Studio atau Microsoft Visual Studio Code dengan Flutter dan Dart extension yang sudah terinstall.

Starter code project tersedia di: https://github.com/rafiadipramana/flutter_deep_link_android

Implementasi pada artikel ini dibagi menjadi 5 TODO:
TODO 1: Menambahkan MaterialApp.router
import 'package:flutter/material.dart';
void main() {
runApp(
const FlutterDeepLinks(),
);
}
class FlutterDeepLinks extends StatelessWidget {
const FlutterDeepLinks({super.key});
@override
Widget build(BuildContext context) {
// TODO 1: Add MaterialApp.router
// TODO 3: Add our AppRouter.routerConfig as the router property
return MaterialApp.router();
}
}
TODO 2: Membuat GoRouter Instance dengan Routes
import 'package:go_router/go_router.dart';
import 'detail_page.dart';
import 'home_page.dart';
class AppRouter {
/// Add go_router as our dependency using this command in the terminal:
/// 'flutter pub add go_router'
/// Add the following import statement to the top of the file
/// import 'package:go_router/go_router.dart';
// TODO 2: Create a static instance of GoRouter and define some routes
static GoRouter routerConfig = GoRouter(
initialLocation: '/',
routes: <GoRoute>[
GoRoute(
path: '/',
name: 'home',
builder: (context, state) {
return const HomePage();
},
routes: <GoRoute>[
GoRoute(
path: 'detail/:username',
name: 'detail',
builder: (context, state) {
return DetailPage(
passedData: {
'pathParameters': state.pathParameters,
'queryParameters': state.uri.queryParameters,
'extra': state.extra,
},
);
},
),
],
),
],
);
}
Nested Routes
Rute bersarang memastikan pengguna tidak dikeluarkan dari aplikasi ketika menekan tombol back pada device jika sebelumnya mengakses Deep linking.
/// Other rest of the code
GoRoute(
path: '/',
name: 'home',
builder: (context, state) {
return const HomePage();
},
routes: <GoRoute>[
/// Back action from detail page will be redirect to parent route (home)
GoRoute(
path: 'detail/:username',
name: 'detail',
/// Other rest of the code
Meneruskan Data dari URL
Meneruskan data yang didapatkan dari path parameters, query parameters, dan extra (jika ada). Mekanisme ini sangat penting agar halaman yang kita tuju menggunakan Deep linking bisa mengekstrak informasi dari link/URL.

/// Other rest of the code
GoRoute(
/// :username as path parameter key
path: 'detail/:username',
name: 'detail',
builder: (context, state) {
return DetailPage(
passedData: {
/// Parse the path parameters (if any)
'pathParameters': state.pathParameters,
/// Parse the query parameters (if any)
'queryParameters': state.uri.queryParameters,
/// Parse the extra parameters (if any)
'extra': state.extra,
},
);
},
),
/// Other rest of the code
TODO 3: Menambahkan routerConfig ke MaterialApp.router
import 'package:flutter/material.dart';
import 'app_router.dart';
void main() {
runApp(
const FlutterDeepLinks(),
);
}
class FlutterDeepLinks extends StatelessWidget {
const FlutterDeepLinks({super.key});
@override
Widget build(BuildContext context) {
// TODO 1: Add MaterialApp.router
// TODO 3: Add our AppRouter.routerConfig as the router property
return MaterialApp.router(
/// Add the routerConfig property
routerConfig: AppRouter.appRouter,
);
}
}
Sekarang, seharusnya kita sudah bisa mencoba menjalankan aplikasi kita. Sesuai konfigurasi rute yang kita telah definisikan sebelumnya pada properti initialLocation, maka rute pertama aplikasi adalah pada path '/' yaitu di HomePage().

TODO 4: Navigasi ke Detail Page dengan Data
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Home Page'),
),
body: SizedBox(
width: double.infinity,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('Welcome to the Home Page :)'),
ElevatedButton(
onPressed: () {
// TODO 4: Navigate to the details page and pass some data
/// Use context.pushNamed to navigate to the details page
context.pushNamed(
/// Use named route "detail" to pass some data
"detail",
/// Pass the path parameters
pathParameters: {
"username": "rafiadipramana",
},
/// Pass the query parameters
queryParameters: {
"lang": "id"
},
/// Pass the extra data
extra: {
"extra": "This is coming from HomePage()",
},
);
},
child: const Text('Go to Details Page'),
),
],
),
),
);
}
}
Penggunaan context.pushNamed merupakan mekanisme navigasi yang disediakan Go Router untuk melakukan push (mendorong/menumpuk) halaman baru di atas halaman sebelumnya — di kasus ini adalah menumpuk halaman detail di atas halaman home.

context.pushNamed dipilih daripada context.push, karena kita dapat menggunakan properti seperti pathParameters, queryParameters, dan extra. Properti-properti tersebut akan dimanfaatkan untuk mengirimkan data ke halaman detail.
TODO 5: Menampilkan Data di Detail Page
import 'package:flutter/material.dart';
class DetailPage extends StatelessWidget {
/// The data passed from the home page, make it nullable
final Map<String, dynamic>? passedData;
const DetailPage({
super.key,
/// Initialize the passedData parameter as not required
this.passedData,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Detail Page'),
),
body: SizedBox(
width: double.infinity,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('Hi, You are on the Detail Page :)'),
// TODO 5: If passedData is not null, display it
Text(
/// Using ternary operator to check if passedData is not null
passedData != null ? passedData.toString() : 'No data passed',
textAlign: TextAlign.center,
),
],
),
),
);
}
}
Penggunaan ternary operator untuk mengecek apakah ada data yang diterima di halaman detail. Jika ada data yang diterima (!= null) = true, maka menampilkan data tersebut. Sebaliknya, jika tidak ada data yang diterima (!= null) = false, maka akan menampilkan teks 'No data passed'.
/// Other rest of the code
// TODO 5: If passedData is not null, display it
Text(
/// Using ternary operator to check if passedData is not null
passedData != null ? passedData.toString() : 'No data passed',
textAlign: TextAlign.center,
),
/// Other rest of the code
Hasil Akhir

Sekarang kita sudah satu langkah lebih dekat untuk bisa mengimplementasikan fitur Deep linking pada aplikasi kita! Silakan ikuti bagian demi bagian dari seri ini ya, terima kasih banyak sudah membaca sampai akhir!
Jika kamu tertarik, merasa kesulitan, atau hanya sekedar ingin terhubung. Boleh banget follow & kontak saya di akun berikut :)
- LinkedIn: https://www.linkedin.com/in/rafiadipramana/
- Instagram: https://www.instagram.com/rafiadipramana/
- GitHub: https://github.com/rafiadipramana