NavController — esensi, metode, dan manajemen navigasi di Jetpack Compose

Penulis: IT Sectr Diterbitkan: 2026-06-29 Waktu membaca: 7 mnt

NavController adalah komponen utama dari pustaka Navigation Compose yang mengelola tumpukan navigasi dan status back stack di aplikasi Android. Melalui NavController dilakukan transisi antar layar, kembali ke halaman sebelumnya, dan pengiriman data antar rute. Menurut Android Developers (2025), NavController adalah elemen wajib dari setiap aplikasi Compose dengan lebih dari satu layar. Controller dibuat melalui rememberNavController(), diteruskan ke NavHost, dan tersedia untuk memanggil navigate() dari titik mana pun dalam komposisi. Dukungan bawaan SavedStateHandle secara otomatis menyimpan status ViewModel saat rekonfigurasi.

Poin Utama

  • NavController — controller navigasi pusat Compose, mengelola back stack dan transisi antar layar
  • navigate() — metode utama untuk transisi ke rute dengan dukungan NavOptions untuk manajemen tumpukan
  • popBackStack() — kembali ke layar sebelumnya dengan pembersihan opsional hingga rute yang ditentukan
  • SavedStateHandle — integrasi dengan ViewModel untuk menyimpan status layar saat navigasi
  • currentBackStackEntryAsState() — observasi rute saat ini untuk sinkronisasi UI

Apa itu NavController di Jetpack Compose?

NavController — adalah kelas dari pustaka Navigation Compose yang mengimplementasikan controller navigasi untuk aplikasi Compose. NavController mengelola tumpukan NavBackStackEntry, di mana setiap entri berisi rute, argumen, dan status layar. Controller mendukung operasi navigasi dasar: transisi, kembali, penggantian, dan pembersihan.

Tidak seperti sistem View, di mana navigasi dilakukan melalui FragmentManager atau Intent, NavController bekerja secara eksklusif dalam konteks Compose. Back stack disimpan dalam bentuk grafik NavDestination, bukan tumpukan Fragment. Ini menghilangkan overhead pembuatan dan penghancuran Fragment, serta menyederhanakan pengujian — NavController dapat di-mock melalui TestNavHostController.

NavController terkait erat dengan NavHost — wadah yang merender layar saat ini dari grafik. Tanpa NavHost, NavController tidak dapat menampilkan fungsi composable, tetapi tetap mempertahankan kemampuan mengelola tumpukan. Dalam arsitektur tipikal, NavController dibuat di tingkat Activity atau composable utama dan diteruskan ke bawah pohon komposisi melalui parameter.

Menurut Google, NavController telah melalui beberapa rilis utama. Versi 2.8.0 menambahkan Type-Safe Navigation, versi 2.9.0 — dukungan untuk predictive back gesture (Android 14+). Controller kompatibel dengan Material3 Scaffold dan BottomNavigation. Untuk proyek multi-modul, NavController diteruskan melalui DI (Hilt/Koin) atau parameter konstruktor.

NavController dibuat melalui fungsi composable rememberNavController(). Fungsi mengembalikan instance NavHostController (turunan NavController) yang terikat dengan siklus hidup composable saat ini. Saat keluar dari komposisi, controller dibersihkan. Untuk menyimpan controller saat rekonfigurasi, gunakan rememberSaveable atau ViewModel.

kotlin
@Composable
fun MyApp() {
    val navController = rememberNavController()
    NavHost(
        navController = navController,
        startDestination = "main"
    ) {
        composable("main") { MainScreen(navController) }
        composable("details") { DetailsScreen(navController) }
    }
}

Konfigurasi NavController meliputi: NavHostController (utama), TestNavHostController (pengujian) dan ScopedNavController (anak untuk grafik bersarang). Untuk BottomNavigation, NavController harus tunggal untuk seluruh aplikasi — membuat controller baru di setiap tab akan menyebabkan hilangnya tumpukan. Untuk meneruskan controller ke layar bersarang, gunakan parameter fungsi, bukan CompositionLocalProvider, untuk menjaga keterbacaan.

Untuk menguji navigasi, gunakan TestNavHostController dengan compose-test-rule. Controller memungkinkan pengaturan rute awal dan verifikasi bahwa navigate() memanggil transisi yang diharapkan. Pengujian NavController tidak memerlukan emulator — bekerja dengan matcher Semantics Compose Test.

Metode navigate(route: String) — cara utama navigasi di NavController. Menerima string rute, NavOptions opsional, dan Navigator.Extras. NavOptions mengelola perilaku transisi: launchSingleTop (jangan duplikasi rute di tumpukan), popUpTo (bersihkan tumpukan hingga rute), restoreState (pulihkan status sebelumnya).

NavOptions diatur melalui sintaks builder: NavOptionsBuilder. Parameter utama: popUpTo (route + inclusive/saveState), launchSingleTop (Boolean, true — jangan buat duplikat), restoreState (pulihkan status saat kembali). Tanpa popUpTo, setiap navigate() menambahkan entri ke tumpukan, yang menyebabkan penumpukan back stack dan perilaku tidak benar dari tombol Back.

kotlin
navController.navigate("profile/42") {
    popUpTo("main") { saveState = true }
    launchSingleTop = true
    restoreState = true
}

Navigator.Extras memungkinkan pengiriman data tambahan yang bukan bagian dari rute: shared element untuk animasi, flag Intent, Pac-Man bundle. Extras jarang digunakan — terutama untuk integrasi dengan Accompanist Animation atau Navigator kustom. Untuk sebagian besar skenario, string rute dan NavOptions sudah cukup.

popBackStack: manajemen kembali dan pembersihan tumpukan

popBackStack() — metode untuk kembali ke layar sebelumnya. Tanpa argumen, menghapus entri teratas tumpukan dan mengembalikan true jika penghapusan berhasil. Jika tumpukan kosong — metode mengembalikan false, dan Activity ditutup (mirip dengan super.onBackPressed()).

Versi kelebihan beban popBackStack(route: String, inclusive: Boolean) menghapus semua entri hingga rute yang ditentukan. Jika inclusive = true — rute yang ditentukan juga dihapus. Metode mengembalikan Boolean — true jika entri ditemukan dan dihapus. Versi dengan inclusive berguna untuk skenario “keluar ke layar utama” setelah otorisasi atau penyelesaian pesanan.

MetodeDeskripsiContoh
popBackStack()Kembali satu layar ke belakangnavController.popBackStack()
popBackStack(route, false)Bersihkan hingga rute (rute tetap)popBackStack("home", false)
popBackStack(route, true)Bersihkan hingga dan termasuk rutepopBackStack("home", true)
navigate(route) { popUpTo(route) { inclusive = true } }Transisi dengan pembersihan penuhnavigate("login") { popUpTo(0) { inclusive = true } }

Untuk menangani tombol Back sistem (hardware back button), gunakan BackHandler dari Compose. BackHandler menerima enabled dan onBack — callback yang dipanggil saat ditekan. Untuk Android 14+ digunakan PredictiveBackGesture, diintegrasikan melalui NavController sejak versi 2.9.0. Predictive back menambahkan animasi pratinjau kembali.

SavedStateHandle: menyimpan status layar

SavedStateHandle — adalah mekanisme untuk menyimpan status ViewModel saat navigasi dan rekonfigurasi. NavController secara otomatis menyediakan SavedStateHandle untuk setiap NavBackStackEntry. Melalui SavedStateHandle, ViewModel menyimpan status layar dan memulihkannya saat kembali (restoreState = true).

Di Navigation Compose, SavedStateHandle digunakan bersama ViewModel: ViewModel diinisialisasi melalui SavedStateHandle yang diteruskan dari backStackEntry. Saat transisi ke layar lain dan kembali (dengan restoreState), ViewModel menerima status yang disimpan, tidak dibuat ulang. Ini sangat penting untuk layar dengan input data, filter, atau scroll.

kotlin
class ProfileViewModel(
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {
    val userId: String = savedStateHandle.get<String>("userId") ?: ""
    var searchQuery by savedStateHandle.getStateFlow("search", "")
        .collectAsState()
}

SavedStateHandle mendukung tipe primitif, String, Bundle, dan Parcelable. Untuk objek kompleks, simpan hanya ID, dan muat data lengkap dari repositori. Batas SavedStateHandle sekitar 1 MB, kelebihan menyebabkan TransactionTooLargeException. Untuk volume besar, gunakan Room atau DataStore daripada menyimpan di handle.

Penting: SavedStateHandle menyimpan status hanya saat menggunakan restoreState = true di NavOptions. Jika restoreState tidak ditentukan, saat kembali ViewModel dibuat ulang dengan nilai default. Untuk perpindahan BottomNavigation dengan restoreState, NavController menyimpan status setiap tab dan memulihkannya saat pemilihan ulang.

Observasi rute saat ini melalui currentBackStackEntryAsState

currentBackStackEntryAsState() — fungsi yang mengembalikan State<NavBackStackEntry?>, yang diperbarui pada setiap perubahan rute saat ini. Ini adalah mekanisme utama sinkronisasi UI dengan navigasi: BottomNavigation menyorot elemen aktif, Toolbar memperbarui judul, Drawer menutup saat transisi.

Fungsi bekerja melalui snapshotFlow dan collectAsState: saat back stack berubah, Compose mengomposisi ulang elemen yang berlangganan. Penting: currentBackStackEntryAsState() diperbarui hanya setelah animasi transisi selesai. Untuk pembaruan segera, gunakan currentDestination, yang berubah secara sinkron dengan navigate(), tetapi tidak mendukung status.

kotlin
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination?.route

Text(
    text = when (currentRoute) {
        "home" -> "Home"
        "profile" -> "Profile"
        else -> ""
    }
)

Untuk mengakses argumen rute saat ini, gunakan navBackStackEntry?.arguments. Ini nyaman di BottomNavigation: selectedItem dihitung berdasarkan currentRoute. Untuk debugging navigasi, gunakan NavController.addOnDestinationChangedListener(), yang mencatat setiap transisi. Dalam produksi, hindari langganan di dalam banyak composable — buat satu sumber di ViewModel dan teruskan State ke UI.

Pertanyaan Umum

Bisakah beberapa NavController dibuat dalam satu Activity?

Secara teknis ya, tetapi tidak disarankan. Satu NavController memastikan back stack yang konsisten dan menyederhanakan debugging. Banyak controller hanya dibenarkan untuk grafik bersarang dengan navigasi terpisah (misalnya, modal bottom sheet dengan tumpukannya sendiri).

Bagaimana cara meneruskan NavController melalui ViewModel?

Teruskan NavController ke ViewModel melalui konstruktor atau DI. Namun, lebih baik hanya meneruskan fungsi callback (onNavigate, onBack), bukan NavController itu sendiri — ini menyederhanakan pengujian. Untuk event, gunakan Channel<NavEvent> di ViewModel dan kumpulkan di UI.

Mengapa navigate tidak berfungsi setelah operasi asinkron?

Masalahnya ada di siklus hidup: jika NavController belum diinisialisasi (NavHost belum dibangun), navigate() diabaikan. Gunakan LaunchedEffect untuk memanggil navigasi setelah data dimuat, bukan di dalam coroutine dengan siklus hidup sembarangan.

Bagaimana cara membersihkan seluruh back stack dan pergi ke layar baru?

Panggil navController.navigate("target") { popUpTo(0) { inclusive = true } }. Parameter popUpTo(0) membersihkan tumpukan sepenuhnya, inclusive = true juga menghapus entri awal. Bendera launchSingleTop = true mencegah duplikasi rute baru.

Apa perbedaan antara NavHostController dan NavController?

NavHostController — turunan NavController dengan metode tambahan untuk NavHost (misalnya, setOnBackStackChangedListener). NavController — kelas dasar yang dapat digunakan di luar NavHost untuk manajemen tumpukan secara terprogram. Dalam kebanyakan kasus, NavHostController digunakan.

Ringkasan

  • NavController — komponen pusat Navigation Compose, mengelola tumpukan rute dan transisi antar layar
  • navigate() melakukan transisi dengan pengaturan popUpTo, launchSingleTop, dan restoreState melalui NavOptions
  • popBackStack() mengelola kembali: langkah tunggal atau pembersihan massal hingga rute yang ditentukan dengan inclusive
  • SavedStateHandle berintegrasi dengan ViewModel untuk penyimpanan otomatis status layar saat navigasi
  • currentBackStackEntryAsState() menyediakan observasi reaktif rute saat ini untuk sinkronisasi UI
  • BackHandler menangani tombol Back sistem, PredictiveBackGesture didukung sejak NavController 2.9.0
  • Untuk pengujian, gunakan TestNavHostController dengan compose-test-rule dan matcher Semantics

Kami akan mengembangkan aplikasi seluler turnkey

IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.

Diskusikan proyek

Baca juga