BottomNavigation: apa itu, struktur dan pengaturan di Jetpack Compose

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

BottomNavigation (NavigationBar di Material3) adalah komponen Material Design yang menyediakan navigasi antara tiga hingga lima layar utama aplikasi. Di Jetpack Compose, panel bawah diimplementasikan melalui NavigationBar atau BottomNavigation, di mana setiap elemen diwakili oleh ikon dan label teks. Menurut Google Material Design (2025), BottomNavigation direkomendasikan untuk iOS dan Android sebagai pola navigasi utama. Panel secara otomatis menyesuaikan dengan tinggi navigasi sistem (Gesture Navigation) melalui WindowInsets. NavigationBar di Material3 mendukung dynamic color, badge, dan jarak adaptif.

Poin utama

  • BottomNavigation — panel navigasi bawah untuk 3–5 layar, diimplementasikan melalui NavigationBar di Material3
  • NavigationBarItem — elemen panel berisi ikon, label, dan lencana untuk notifikasi
  • Koneksi dengan NavHost — pemilihan elemen memicu navigate() di NavController untuk berpindah grafik
  • Material3 menambahkan Dynamic Color dan perhitungan otomatis navigasi sistem melalui WindowInsets
  • Badge — lencana notifikasi pada ikon, diimplementasikan melalui BadgedBox dengan angka

Apa itu BottomNavigation di Jetpack Compose?

BottomNavigation adalah komponen Material Design untuk panel navigasi bawah yang menampilkan tiga hingga lima item menu. Di Jetpack Compose, ini diimplementasikan melalui fungsi composable BottomNavigation (Material2) dan NavigationBar (Material3). Panel ditempatkan di bagian bawah layar dan menyediakan akses cepat ke bagian-bagian utama aplikasi.

Tugas utama BottomNavigation adalah mengatur navigasi primer antara tingkat layar. Setiap elemen panel mewakili layar terpisah (Home, Search, Profile). Tekanan mengalihkan konten di area utama layar. BottomNavigation tidak ditujukan untuk navigasi bersarang — untuk itu digunakan tab (Tabs) di bagian atas.

Menurut Material Design Guidelines (2025), panel bawah hanya boleh berisi bagian-bagian yang dapat diakses dari titik mana pun dalam aplikasi. Menyembunyikan panel di layar anak (misalnya, saat membuka halaman detail) adalah praktik standar. Di Compose, visibilitas BottomNavigation dikelola melalui status Scaffold atau rendering bersyarat berdasarkan rute NavController saat ini.

Di Material3, dianjurkan menggunakan NavigationBar daripada Legacy BottomNavigation. NavigationBar mencakup Dynamic Color, dukungan WindowInsets, dan animasi tekanan yang lebih baik. Menurut Google, NavigationBar adalah komponen prospektif, sedangkan BottomNavigation dari M2 tetap hanya untuk kompatibilitas mundur.

Setiap elemen NavigationBar diimplementasikan melalui composable NavigationBarItem. Parameter elemen: icon (ikon), label (label teks), selected (status pemilihan) dan onClick (panggilan balik). NavigationBarItem secara otomatis menerapkan warna yang dipilih (primary) dan animasi perubahan ukuran ikon saat aktivasi.

kotlin
@Composable
fun BottomNavBar() {
    val items = listOf(
        Screen.Home,
        Screen.Search,
        Screen.Profile
    )
    var selectedItem by remember { mutableIntStateOf(0) }
    NavigationBar {
        items.forEachIndexed { index, item ->
            NavigationBarItem(
                icon = { Icon(item.icon, contentDescription = item.label) },
                label = { Text(item.label) },
                selected = selectedItem == index,
                onClick = { selectedItem = index }
            )
        }
    }
}

Ikon untuk NavigationBarItem harus Filled untuk status dipilih dan Outlined untuk status tidak dipilih. Material Icons menyediakan kedua set: Icons.Filled.Home dan Icons.Outlined.Home. Secara default, NavigationBarItem menampilkan varian filled untuk selected. Jika tampilan berbeda diperlukan, berikan ikon secara bersyarat: if (selected) Filled.X else Outlined.X.

Panjang label teks elemen — tidak lebih dari 12 karakter (alfabet Latin) atau 8 karakter (Sirilik). Label panjang dipotong dengan elipsis. Untuk label, gunakan maxLines = 1 dan ellipsis = TextTruncation.Ellipsis. Jika label selalu diperlukan, atur parameter alwaysShowLabel = true (default true).

ParameterTipeDeskripsi
icon@Composable () -> UnitIkon elemen panel
label@Composable (() -> Unit)?Label teks di bawah ikon
selectedBooleanBendera elemen yang dipilih
onClick() -> UnitPanggilan balik saat ditekan
badge@Composable (() -> Unit)?Lencana dengan jumlah notifikasi

Menghubungkan BottomNavigation dengan NavHost

Pembaruan langsung selectedItem tidak mengarahkan navigasi — untuk mengganti layar diperlukan integrasi dengan NavController. Pola tipikal: onClick di NavigationBarItem memanggil navController.navigate(route), dan selectedItem ditentukan dari rute NavController saat ini.

NavController menyimpan back stack saat ini. Untuk menentukan elemen panel mana yang dipilih, bandingkan rute saat ini (navController.currentBackStackEntryAsState().value?.destination?.route) dengan rute elemen. Jika rute cocok — elemen dianggap dipilih. Ini menghilangkan duplikasi status.

kotlin
@Composable
fun MainScreen(navController: NavController) {
    val navBackStackEntry by navController.currentBackStackEntryAsState()
    val currentRoute = navBackStackEntry?.destination?.route
    Scaffold(
        bottomBar = {
            NavigationBar {
                items.forEach { item ->
                    NavigationBarItem(
                        icon = { Icon(item.icon, contentDescription = item.label) },
                        label = { Text(item.label) },
                        selected = currentRoute == item.route,
                        onClick = {
                            navController.navigate(item.route) {
                                popUpTo(navController.graph.findStartDestination().id) {
                                    saveState = true
                                }
                                launchSingleTop = true
                                restoreState = true
                            }
                        }
                    )
                }
            }
        }
    ) { /* content */ }
}

Pengaturan kunci dalam navigate(): popUpTo(startDestination) mencegah penumpukan back stack saat mengganti tab, launchSingleTop mengecualikan duplikasi rute dalam tumpukan, dan restoreState mengembalikan status kunjungan sebelumnya ke tab. Tanpa parameter ini, setiap tekanan membuat entri baru dalam tumpukan, yang menyebabkan perilaku tombol Kembali yang tidak benar.

NavigationBar di Material3: Dynamic Color dan adaptasi

Material3 (M3) menyediakan NavigationBar dan NavigationBarItem sebagai pengganti BottomNavigation dari Material2. Keunggulan utama M3: Dynamic Color, tinggi adaptif, animasi ripple selebar elemen, dan dukungan WindowInsets untuk memperhitungkan navigasi sistem.

Dynamic Color secara otomatis memilih warna NavigationBar berdasarkan wallpaper perangkat (Android 12+). Elemen aktif diwarnai primary, yang tidak aktif — onSurfaceVariant. Secara default, NavigationBar naik di atas navigasi sistem melalui Modifier.navigationBarsPadding(). Untuk kontrol manual, gunakan parameter windowInsets.

NavigationBar di M3 secara otomatis menganimasikan lebar elemen aktif — ikon bergeser ke kiri dan label meluas. Animasi berlangsung 300 ms dan menggunakan easing standar Compose. Jika perilaku ini tidak diinginkan, atur label selalu terlihat melalui alwaysShowLabel = true di NavigationBarItem.

Kustomisasi warna NavigationBar

Untuk mengganti warna, berikan NavigationBarColors dalam parameter colors. Secara default, NavigationBarDefaults.colors() digunakan, tetapi Anda dapat mengatur warna sendiri: containerColor (latar panel), contentColor (warna ikon), indicatorColor (latar elemen aktif). Berbeda dengan M2, di mana warna diatur melalui BottomNavigationDefaults, di M3 digunakan antarmuka seragam NavColors.

Jika dalam proyek digunakan BottomNavigation dari M2 (androidx.compose.material), Google merekomendasikan migrasi ke M3 NavigationBar. Migrasi mencakup penggantian impor, pembaruan parameter, dan penambahan @OptIn(ExperimentalMaterial3Api::class). BottomNavigation dari M2 dianggap usang dan tidak menerima fitur baru.

Status NavigationBar menyimpan indeks elemen yang sedang dipilih. Dalam kasus sederhana, remember { mutableIntStateOf(0) } digunakan. Namun, untuk aplikasi produksi, indeks harus disinkronkan dengan grafik navigasi, bukan menjadi status UI lokal.

Pendekatan utama untuk manajemen status: NavController-based (direkomendasikan) dan ViewModel-based. Dalam kasus pertama, selectedItem dihitung dari rute NavController saat ini. Dalam kasus kedua — ViewModel menyimpan layar saat ini, dan NavigationBarItem membaca StateFlow dari ViewModel. NavController-based lebih sederhana, ViewModel-based memberikan lebih banyak kontrol dalam logika kompleks (otorisasi, tes A/B).

Saat menggunakan saveState dan restoreState, NavController secara otomatis menyimpan dan mengembalikan status LazyColumn dan komponen lain di dalam setiap tab. Ini menghilangkan kebutuhan ViewModel untuk menyimpan posisi scroll. Namun, penyimpanan tidak berlaku untuk bidang input — mereka memerlukan SavedStateHandle atau ViewModel.

Lencana notifikasi dan penghitung pada elemen

Badge adalah indikator notifikasi kompak yang ditampilkan pada ikon NavigationBarItem. Di Material3, lencana diimplementasikan melalui BadgedBox, yang membungkus ikon dan menempatkan lencana di sudut kanan atas. Badge dapat berisi angka (jumlah notifikasi) atau titik (indikasi keberadaan sederhana).

kotlin
@Composable
fun HomeItem(unreadCount: Int) {
    NavigationBarItem(
        icon = {
            BadgedBox(badge = {
                if (unreadCount > 0) {
                    Badge { Text(unreadCount.toString()) }
                }
            }) {
                Icon(Icons.Filled.Home, contentDescription = "Beranda")
            }
        },
        label = { Text("Home") },
        selected = false,
        onClick = { }
    )
}

BadgedBox secara otomatis memposisikan Badge di sudut kanan atas elemen anak. Jika angka melebihi 99, Badge menampilkan “99+”. Untuk menyembunyikan lencana saat nilai nol, gunakan kondisi if (count > 0). Badge dari Material3 secara default memiliki warna error (merah) dengan teks putih. Kustomisasi melalui BadgeDefaults.colors(containerColor, contentColor).

Di Material2, lencana tidak ada — harus digambar manual melalui Canvas atau menggunakan pustaka pihak ketiga. Material3 memecahkan masalah ini dengan komponen asli. Menurut Android Developers (2025), BadgedBox digunakan dalam 40% aplikasi dengan NavigationBar, di mana 60% digunakan untuk menampilkan pesan yang belum dibaca.

Pertanyaan yang Sering Diajukan

Berapa banyak elemen yang harus ada di BottomNavigation?

Material Design merekomendasikan 3 hingga 5 elemen. Kurang dari tiga — tidak berguna (lebih baik menggunakan Tabs). Lebih dari lima — mengurangi keterbacaan label dan menurunkan touch-target. Jika diperlukan lebih dari 5 bagian, gunakan Navigation Rail (tablet) atau Drawer.

Bagaimana cara menyembunyikan BottomNavigation di layar anak?

Tentukan daftar rute di mana panel terlihat (biasanya rute root). Di Scaffold bottomBar, berikan NavigationBar hanya jika currentRoute in bottomNavRoutes. Alternatif — NavHost dengan grafik terpisah untuk layar bersarang tanpa bottomBar.

Apa perbedaan antara NavigationBar (M3) dan BottomNavigation (M2)?

NavigationBar dari Material3 mendukung Dynamic Color, BadgedBox, WindowInsets, dan lebar animasi elemen aktif. BottomNavigation dari Material2 adalah API lama tanpa fitur ini. Google merekomendasikan menggunakan NavigationBar untuk proyek baru dan migrasi proyek yang ada.

Mengapa layar dibuat ulang saat menekan elemen BottomNavigation?

Masalahnya adalah tidak adanya saveState dan restoreState di navigate(). Tanpa mereka, NavController tidak menyimpan status tab sebelumnya. Tambahkan popUpTo(startDestination) { saveState = true } dan restoreState = true di NavOptions untuk mempertahankan LazyColumn, scroll, dan bidang input.

Bagaimana cara menyesuaikan animasi perpindahan antar tab?

Animasi perpindahan dikendalikan di tingkat NavHost, bukan BottomNavigation. Tambahkan composable(route, enterTransition, exitTransition) dengan animasi kustom. Untuk BottomNavigation, animasi fading (fadeIn + fadeOut) dengan durasi 300ms adalah tipikal.

Ringkasan

  • BottomNavigation (NavigationBar di M3) — panel bawah untuk beralih antara 3–5 layar utama aplikasi
  • NavigationBarItem — elemen panel dengan ikon, label, lencana, dan status pemilihan
  • Integrasi dengan NavController memerlukan popUpTo, launchSingleTop, dan restoreState untuk manajemen tumpukan yang benar
  • Material3 NavigationBar mendukung Dynamic Color, BadgedBox, WindowInsets, dan animasi elemen aktif
  • BadgedBox — komponen untuk lencana notifikasi dengan angka atau titik pada ikon elemen
  • Status pemilihan dihitung dari currentBackStackEntryAsState untuk sinkronisasi dengan NavController
  • Untuk menyembunyikan panel di layar anak, gunakan rendering bersyarat di Scaffold bottomBar berdasarkan daftar rute

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