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 — 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.
@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.
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() — 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.
| Metode | Deskripsi | Contoh |
|---|---|---|
| popBackStack() | Kembali satu layar ke belakang | navController.popBackStack() |
| popBackStack(route, false) | Bersihkan hingga rute (rute tetap) | popBackStack("home", false) |
| popBackStack(route, true) | Bersihkan hingga dan termasuk rute | popBackStack("home", true) |
| navigate(route) { popUpTo(route) { inclusive = true } } | Transisi dengan pembersihan penuh | navigate("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 — 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.
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.
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.
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
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).
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.
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.
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.
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
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.
Baca juga