Komponen Navigasi menyediakan bahasa khusus domain, atau DSL, berbasis Kotlin yang bergantung pada type-safe builder Kotlin.
API ini memungkinkan Anda membuat grafik secara deklaratif di kode Kotlin, bukan di dalam resource XML. Cara ini dapat berguna jika Anda ingin membuat navigasi aplikasi secara dinamis. Misalnya, aplikasi Anda dapat mendownload dan meng-cache konfigurasi navigasi dari layanan web eksternal, lalu menggunakan konfigurasi tersebut untuk membuat grafik navigasi secara dinamis dalam fungsi onCreate()
aktivitas Anda.
Dependensi
Untuk menggunakan DSL Kotlin, tambahkan dependensi berikut ke file build.gradle
aplikasi Anda:
Groovy
dependencies { def nav_version = "2.5.3" api "androidx.navigation:navigation-fragment-ktx:$nav_version" }
Kotlin
dependencies { val nav_version = "2.5.3" api("androidx.navigation:navigation-fragment-ktx:$nav_version") }
Membuat grafik
Mari kita mulai dengan contoh sederhana berdasarkan aplikasi Sunflower. Untuk contoh ini, kita memiliki dua tujuan: home
dan plant_detail
. Tujuan home
ada saat pengguna pertama kali meluncurkan aplikasi. Tujuan ini menampilkan daftar tanaman dari taman pengguna. Saat pengguna memilih salah satu tanaman, aplikasi akan menavigasi ke tujuan plant_detail
.
Gambar 1 menunjukkan tujuan berikut beserta argumen yang diperlukan oleh tujuan plant_detail
dan tindakan to_plant_detail
yang digunakan aplikasi untuk menavigasi dari home
ke plant_detail
.

home
dan plant_detail
, bersama dengan tindakan yang menghubungkan keduanya.Membuat host untuk grafik navigasi DSL Kotlin
Terlepas dari cara membuat grafik, Anda perlu menghosting grafik dalam NavHost
.
Sunflower menggunakan fragmen, jadi mari kita menggunakan NavHostFragment
di dalam FragmentContainerView
, seperti yang ditunjukkan pada contoh berikut:
<!-- activity_garden.xml -->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true" />
</FrameLayout>
Perhatikan bahwa atribut app:navGraph
tidak ditetapkan dalam contoh ini, karena grafik dibuat secara terprogram dan bukan ditetapkan sebagai resource XML.
Membuat konstanta untuk grafik
Saat menangani grafik navigasi berbasis XML, proses build Android menguraikan file resource grafik dan menentukan konstanta numerik untuk setiap atribut id
yang ditentukan dalam grafik. Konstanta ini dapat diakses dalam kode Anda melalui class resource yang dihasilkan, R.id
.
Misalnya, cuplikan grafik XML berikut mendeklarasikan tujuan fragmen dengan id
, home
:
<navigation ...>
<fragment android:id="@+id/home" ... />
...
</navigation>
Proses build menghasilkan nilai konstanta R.id.home
yang dikaitkan dengan tujuan ini. Selanjutnya, Anda dapat mereferensikan tujuan tersebut dari kode Anda dengan menggunakan nilai konstanta ini.
Proses penguraian dan pembuatan konstanta tidak terjadi saat Anda membuat grafik secara terprogram menggunakan DSL Kotlin. Sebaliknya, Anda harus menentukan konstanta Anda sendiri untuk setiap tujuan, tindakan, dan argumen yang memiliki nilai id
. Setiap ID harus unik dan konsisten di seluruh perubahan konfigurasi.
Satu cara yang teratur untuk membuat konstanta adalah membuat set bertingkat object
Kotlin yang menentukan konstanta secara statis, seperti yang ditunjukkan pada contoh berikut:
object nav_graph {
const val id = 1 // graph id
object dest {
const val home = 2
const val plant_detail = 3
}
object action {
const val to_plant_detail = 4
}
object args {
const val plant_id = "plantId"
}
}
Dengan struktur ini, Anda dapat mengakses nilai ID dalam kode dengan menautkan panggilan objek secara bersamaan, seperti yang ditunjukkan pada contoh berikut:
nav_graph.id // graph id
nav_graph.dest.home // home destination id
nav_graph.action.to_plant_detail // action home -> plant_detail id
nav_graph.args.plant_id // destination argument name
Membuat grafik dengan NavGraphBuilder DSL
Setelah menentukan kumpulan awal ID, Anda dapat membuat grafik navigasi. Gunakan fungsi ekstensi NavController.createGraph()
untuk membuat NavGraph
, yang meneruskan id
untuk grafik Anda, nilai ID untuk startDestination
, dan lambda di akhir yang menentukan struktur grafik Anda.
Anda dapat membuat grafik pada fungsi onCreate()
aktivitas Anda.
createGraph()
menampilkan Navgraph
yang kemudian dapat Anda tetapkan ke properti graph
dari NavController
yang terkait dengan NavHost
, seperti yang ditunjukkan pada contoh berikut:
class GardenActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_garden)
val navHostFragment = supportFragmentManager
.findFragmentById(R.id.nav_host) as NavHostFragment
navHostFragment.navController.apply {
graph = createGraph(nav_graph.id, nav_graph.dest.home) {
fragment<HomeViewPagerFragment>(nav_graph.dest.home) {
label = getString(R.string.home_title)
action(nav_graph.action.to_plant_detail) {
destinationId = nav_graph.dest.plant_detail
}
}
fragment<PlantDetailFragment>(nav_graph.dest.plant_detail) {
label = getString(R.string.plant_detail_title)
argument(nav_graph.args.plant_id) {
type = NavType.StringType
}
}
}
}
}
}
Dalam contoh ini, lambda di akhir mendefinisikan dua tujuan fragmen menggunakan fungsi builder DSL fragment()
. Fungsi ini memerlukan ID untuk tujuan. Fungsi tersebut juga menerima lambda opsional untuk konfigurasi tambahan, seperti tujuan label
, serta fungsi builder tersemat untuk tindakan, argumen, dan deep link.
Class Fragment
yang mengelola UI setiap tujuan diteruskan sebagai jenis yang diparameterisasi dalam tanda kurung sudut (<>
). Ini memiliki efek yang sama seperti menetapkan atribut android:name
pada tujuan fragmen yang ditentukan menggunakan XML.
Menavigasi dengan grafik DSL Kotlin
Setelah membuat dan menetapkan grafik, Anda dapat menavigasi dari home
ke plant_detail
menggunakan NavController.navigate()
, seperti yang ditunjukkan pada contoh berikut:
private fun navigateToPlant(plantId: String) {
val args = bundleOf(nav_graph.args.plant_id to plantId)
findNavController().navigate(nav_graph.action.to_plant_detail, args)
}
Jenis tujuan yang didukung
DSL Kotlin mendukung tujuan Fragment
, Activity
, dan NavGraph
, masing-masing memiliki fungsi ekstensi inline sendiri yang tersedia untuk membuat dan mengonfigurasi tujuan.
Tujuan fragmen
Fungsi DSL fragment()
dapat diparameterisasi untuk class Fragment
yang mengimplementasi. Fungsi tersebut memerlukan ID unik untuk ditetapkan ke tujuan ini bersama dengan lambda tempat Anda dapat memberikan konfigurasi tambahan.
fragment<FragmentDestination>(nav_graph.dest.fragment_dest_id) {
label = getString(R.string.fragment_title)
// arguments, actions, deepLinks...
}
Tujuan aktivitas
Fungsi DSL activity()
memerlukan ID unik untuk ditetapkan ke tujuan ini, tetapi tidak diparameterisasi ke class yang mengimplementasi aktivitas apa pun. Sebaliknya, Anda dapat menetapkan activityClass
opsional pada lambda di akhir. Fleksibilitas ini memungkinkan Anda menentukan tujuan aktivitas untuk aktivitas yang diluncurkan dari intent implisit, apabila class aktivitas eksplisit tidak memungkinkan. Seperti tujuan fragmen, Anda juga dapat menentukan serta mengonfigurasi label dan argumen apa pun.
activity(nav_graph.dest.activity_dest_id) {
label = getString(R.string.activity_title)
// arguments, actions, deepLinks...
activityClass = ActivityDestination::class
}
Tujuan grafik navigasi
Anda dapat menggunakan fungsi DSL navigation()
untuk membuat grafik navigasi bertingkat.
Seperti jenis tujuan lainnya, fungsi DSL ini membutuhkan tiga argumen: ID untuk ditetapkan ke grafik, ID tujuan awal untuk grafik, dan lambda untuk mengonfigurasi grafik lebih lanjut. Elemen yang valid untuk lambda mencakup argumen, tindakan, tujuan lain, deep link, dan label.
navigation(nav_graph.dest.nav_graph_dest, nav_graph.dest.start_dest) {
// label, arguments, actions, other destinations, deep links
}
Mendukung tujuan kustom
Anda dapat menggunakan addDestination()
untuk menambahkan jenis tujuan kustom ke DSL Kotlin Anda yang tidak didukung secara default, seperti yang ditunjukkan pada contoh berikut:
// The NavigatorProvider is retrieved from the NavController
val customDestination = navigatorProvider[CustomNavigator::class].createDestination().apply {
id = nav_graph.dest.custom_dest_id
}
addDestination(customDestination)
Anda juga dapat menggunakan operator plus unary (+
) untuk menambahkan tujuan yang baru dibuat langsung ke grafik:
// The NavigatorProvider is retrieved from the NavController
+navigatorProvider[CustomNavigator::class].createDestination().apply {
id = nav_graph.dest.custom_dest_id
}
Menyediakan argumen tujuan
Anda dapat menentukan argumen opsional atau wajib untuk jenis tujuan apa pun. Untuk menentukan argumen, panggil fungsi argument()
pada NavDestinationBuilder
, yaitu class dasar untuk semua jenis builder tujuan. Fungsi ini mengambil nama argumen sebagai String
dan lambda yang dapat Anda gunakan untuk membuat dan mengonfigurasi NavArgument
.
Di dalam lambda, Anda dapat menentukan jenis data argumen, nilai default jika berlaku, dan apakah nilai argumen dapat menjadi null
.
fragment<PlantDetailFragment>(nav_graph.dest.plant_detail) {
label = getString(R.string.plant_details_title)
argument(nav_graph.args.plant_name) {
type = NavType.StringType
defaultValue = getString(R.string.default_plant_name)
nullable = true // default false
}
}
Jika defaultValue
diberikan, maka type
bersifat opsional. Dalam hal ini, jika tidak ada type
yang ditentukan, jenis akan disimpulkan dari defaultValue
. Jika defaultValue
dan type
tersedia, jenisnya harus cocok. Untuk daftar lengkap jenis argumen, lihat NavType
.
Tindakan
Anda dapat menentukan tindakan dalam tujuan apa pun, termasuk tindakan global dalam grafik navigasi root. Untuk menentukan tindakan, gunakan fungsi NavDestinationBuilder.action()
, dengan memberikan ID ke fungsi dan lambda untuk menyediakan konfigurasi tambahan.
Contoh berikut membuat tindakan dengan destinationId
, animasi transisi, dan perilaku pop serta single-top.
action(nav_graph.action.to_plant_detail) {
destinationId = nav_graph.dest.plant_detail
navOptions {
anim {
enter = R.anim.nav_default_enter_anim
exit = R.anim.nav_default_exit_anim
popEnter = R.anim.nav_default_pop_enter_anim
popExit = R.anim.nav_default_pop_exit_anim
}
popUpTo(nav_graph.dest.start_dest) {
inclusive = true // default false
}
// if popping exclusively, you can specify popUpTo as
// a property. e.g. popUpTo = nav_graph.dest.start_dest
launchSingleTop = true // default false
}
}
Deep link
Anda dapat menambahkan deep link ke tujuan apa pun, sama seperti grafik navigasi berbasis XML. Prosedur yang sama seperti yang ditentukan dalam Membuat deep link untuk tujuan berlaku untuk proses pembuatan deep link eksplisit menggunakan DSL Kotlin.
Namun, saat membuat deep link implisit, Anda tidak memiliki resource navigasi XML yang dapat dianalisis untuk elemen <deepLink>
. Oleh karena itu, Anda tidak dapat mengandalkan penempatan elemen <nav-graph>
di file AndroidManifest.xml
dan harus secara manual menambahkan filter intent ke aktivitas Anda. Filter intent yang Anda sediakan harus cocok dengan pola URL dasar dari deep link aplikasi Anda.
Untuk setiap tujuan yang diberi deep link satu per satu, Anda dapat menyediakan pola URI yang lebih spesifik menggunakan fungsi DSL deepLink()
. Fungsi ini menerima String
untuk pola URI, seperti yang ditunjukkan pada contoh berikut:
deepLink("http://www.example.com/plants/")
Tidak ada batasan jumlah URI deep link yang dapat Anda tambahkan. Setiap panggilan ke deepLink()
akan menambahkan deep link baru ke daftar internal yang spesifik untuk tujuan tersebut.
Berikut adalah skenario deep link implisit yang lebih kompleks yang juga menentukan parameter berbasis jalur dan kueri:
val baseUri = "http://www.example.com/plants"
fragment<PlantDetailFragment>(nav_graph.dest.plant_detail) {
label = getString(R.string.plant_details_title)
deepLink("${baseUri}/{id}")
deepLink("${baseUri}/{id}?name={plant_name}")
argument(nav_graph.args.plant_id) {
type = NavType.IntType
}
argument(nav_graph.args.plant_name) {
type = NavType.StringType
nullable = true
}
}
Perhatikan bahwa jenis interpolasi string dapat digunakan untuk menyederhanakan definisi.
Membuat ID
Library Navigasi mewajibkan nilai ID yang digunakan untuk elemen grafik adalah bilangan bulat unik yang tetap konstan selama perubahan konfigurasi. Satu cara untuk membuat ID ini adalah dengan menentukannya sebagai konstanta statis seperti yang ditunjukkan pada Membuat konstanta untuk grafik Anda. Anda juga dapat menentukan ID resource statis dalam XML sebagai resource. Atau, Anda dapat membuat ID secara dinamis. Misalnya, Anda dapat membuat penghitung urutan yang bertambah setiap kali Anda mereferensikannya.
object nav_graph {
// Counter for id's. First ID will be 1.
var id_counter = 1
val id = id_counter++
object dest {
val home = id_counter++
val plant_detail = id_counter++
}
object action {
val to_plant_detail = id_counter++
}
object args {
const val plant_id = "plantId"
}
}
Batasan
- Plugin Safe Args tidak kompatibel dengan Kotlin DSL, karena plugin tersebut mencari file resource XML untuk menghasilkan class
Directions
danArguments
.