BottomNavigation (Material3'te NavigationBar), bir uygulamanın üç ila beş ana ekranı arasında gezinme sağlayan bir Material Design bileşenidir. Jetpack Compose'da alt çubuk, NavigationBar veya BottomNavigation aracılığıyla uygulanır ve her öğe bir simge ve metin etiketiyle temsil edilir. Google Material Design'a (2025) göre BottomNavigation, iOS ve Android için birincil gezinme deseni olarak önerilir. Panel, WindowInsets aracılığıyla sistem gezinme (Gesture Navigation) yüksekliğine otomatik olarak uyum sağlar. Material3'te NavigationBar, dinamik renk, rozet ve uyarlanabilir kenar boşluklarını destekler.
Önemli Noktalar
BottomNavigation, üç ila beş menü öğesini görüntüleyen bir alt gezinme çubuğu için Material Design bileşenidir. Jetpack Compose'da BottomNavigation (Material2) ve NavigationBar (Material3) composable işlevleri aracılığıyla uygulanır. Çubuk, ekranın alt kısmına yerleştirilir ve uygulamanın ana bölümlerine hızlı erişim sağlar.
BottomNavigation'ın ana amacı, ekran düzeyindeki hedefler arasında birincil gezinmeyi organize etmektir. Her panel öğesi ayrı bir ekranı (Ana Sayfa, Arama, Profil) temsil eder. Dokunulduğunda ekranın ana alanındaki içerik değişir. BottomNavigation iç içe gezinme için tasarlanmamıştır — bunun için üst kısımda sekmeler kullanılır.
Material Design Kılavuzlarına (2025) göre alt çubuk, yalnızca uygulamanın herhangi bir noktasından erişilebilen bölümleri içermelidir. Alt ekranlarda çubuğu gizlemek (örneğin, bir detay sayfası açarken) standart bir uygulamadır. Compose'da BottomNavigation görünürlüğü, Scaffold durumu veya mevcut NavController rotasına dayalı koşullu oluşturma yoluyla kontrol edilir.
Material3'te, eski BottomNavigation yerine NavigationBar kullanılması önerilir. NavigationBar, Dinamik Renk, WindowInsets desteği ve geliştirilmiş basma animasyonu içerir. Google'a göre NavigationBar geleceğe yönelik bileşendir, M2'deki BottomNavigation ise yalnızca geriye dönük uyumluluk için kalır.
Her NavigationBar öğesi, NavigationBarItem composable'ı aracılığıyla uygulanır. Parametreler: icon (simge), label (metin etiketi), selected (seçim durumu) ve onClick (geri çağrı). NavigationBarItem, seçilen rengi (primary) otomatik olarak uygular ve etkinleştirmede simge boyut değişimini canlandırır.
@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 }
)
}
}
}
NavigationBarItem için simgeler, seçili durum için Dolu (Filled) ve seçili olmayan durum için Anahat (Outlined) olmalıdır. Material Icons her iki seti de sağlar: Icons.Filled.Home ve Icons.Outlined.Home. Varsayılan olarak NavigationBarItem, seçili için dolu varyantı gösterir. Farklı bir görüntüleme gerekiyorsa, simgeyi koşullu olarak iletin: if (selected) Filled.X else Outlined.X.
Metin etiketinin uzunluğu 12 karakteri (Latin) veya 8 karakteri (Kiril) aşmamalıdır. Uzun etiketler üç nokta ile kesilir. Etiketler için maxLines = 1 ve ellipsis = TextTruncation.Ellipsis kullanın. Etiket her zaman gösterilmeliyse, alwaysShowLabel = true parametresini kaldırın (varsayılan true'dur).
| Parametre | Tür | Açıklama |
|---|---|---|
| icon | @Composable () -> Unit | Panel öğesinin simgesi |
| label | @Composable (() -> Unit)? | Simgenin altındaki metin etiketi |
| selected | Boolean | Seçili öğe bayrağı |
| onClick | () -> Unit | Tıklamada geri çağrı |
| badge | @Composable (() -> Unit)? | Bildirim sayısı ile rozet |
selectedItem'ı doğrudan güncellemek gezinmeyi tetiklemez — ekran değiştirmek için NavController ile entegrasyon gerekir. Tipik desen: NavigationBarItem'daki onClick, navController.navigate(route) çağırır ve selectedItem, mevcut NavController rotasından belirlenir.
NavController, mevcut geri yığınını saklar. Hangi panel öğesinin seçili olduğunu belirlemek için mevcut rotayı (navController.currentBackStackEntryAsState().value?.destination?.route) öğe rotalarıyla karşılaştırın. Rota eşleşirse, öğe seçili kabul edilir. Bu, durum tekrarını ortadan kaldırır.
@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 */ }
}
navigate() içindeki anahtar ayarlar: popUpTo(startDestination) sekmeler arasında geçiş yaparken geri yığın birikimini önler, launchSingleTop yığında rota tekrarını önler ve restoreState önceki ziyaret edilen sekmenin durumunu geri yükler. Bu parametreler olmadan, her tıklama yığında yeni bir giriş oluşturur ve bu da Geri düğmesinin yanlış davranmasına yol açar.
Material3 (M3), Material2'deki BottomNavigation'ın yerine NavigationBar ve NavigationBarItem sağlar. M3'ün ana avantajları: Dinamik Renk, uyarlanabilir yükseklik, tam genişlik dalga animasyonu ve sistem gezinme için WindowInsets desteği.
Dinamik Renk, cihaz duvar kağıdına (Android 12+) dayalı olarak NavigationBar renklerini otomatik olarak seçer. Aktif öğe primary renginde, pasif öğeler onSurfaceVariant renginde renklendirilir. Varsayılan olarak NavigationBar, Modifier.navigationBarsPadding() aracılığıyla sistem gezinmesinin üzerine yükselir. Manuel kontrol için windowInsets parametresini kullanın.
M3'te NavigationBar, aktif öğe genişliğini otomatik olarak canlandırır — simge sola kayar ve etiket genişler. Animasyon 300 ms sürer ve standart Compose easing kullanır. Bu davranış istenmiyorsa, NavigationBarItem'da alwaysShowLabel = true ile etiketi her zaman gösterin.
Renkleri geçersiz kılmak için colors parametresinde NavigationBarColors iletin. Varsayılan olarak NavigationBarDefaults.colors() kullanılır, ancak özel değerler ayarlanabilir: containerColor (çubuk arka planı), contentColor (simge renkleri), indicatorColor (aktif öğe arka planı). Renklerin BottomNavigationDefaults aracılığıyla ayarlandığı M2'nin aksine M3, birleşik bir NavColors arayüzü kullanır.
Proje M2'den (androidx.compose.material) BottomNavigation kullanıyorsa, Google M3 NavigationBar'a geçiş yapılmasını önerir. Geçiş, içe aktarmaların değiştirilmesini, parametrelerin güncellenmesini ve @OptIn(ExperimentalMaterial3Api::class) eklenmesini içerir. M2'deki BottomNavigation kullanımdan kaldırılmış olarak kabul edilir ve yeni özellikler almaz.
NavigationBar durumu, mevcut seçili öğenin dizinini saklar. Basit durumlarda, remember { mutableIntStateOf(0) } kullanılır. Ancak, üretim uygulamaları için dizin, gezinme grafiğiyle senkronize edilmelidir, yerel bir UI durumu olmamalıdır.
Durum yönetimine ana yaklaşımlar: NavController tabanlı (önerilen) ve ViewModel tabanlı. İlk durumda, selectedItem mevcut NavController rotasından hesaplanır. İkincisinde, ViewModel mevcut ekranı saklar ve NavigationBarItem, ViewModel'den StateFlow'u okur. NavController tabanlı daha basittir; ViewModel tabanlı, karmaşık mantık (kimlik doğrulama, A/B testleri) için daha fazla kontrol sağlar.
saveState ve restoreState kullanırken, NavController her sekme içindeki LazyColumn ve diğer bileşenlerin durumunu otomatik olarak kaydeder ve geri yükler. Bu, kaydırma konumunu saklamak için bir ViewModel ihtiyacını ortadan kaldırır. Ancak, kaydetme giriş alanları için geçerli değildir — bunlar SavedStateHandle veya ViewModel gerektirir.
Badge, NavigationBarItem simgesinde görüntülenen kompakt bir bildirim göstergesidir. Material3'te rozet, simgeyi saran ve rozeti sağ üst köşeye yerleştiren BadgedBox aracılığıyla uygulanır. Rozet bir sayı (bildirim sayısı) veya bir nokta (basit varlık göstergesi) içerebilir.
@Composable
fun HomeItem(unreadCount: Int) {
NavigationBarItem(
icon = {
BadgedBox(badge = {
if (unreadCount > 0) {
Badge { Text(unreadCount.toString()) }
}
}) {
Icon(Icons.Filled.Home, contentDescription = "Ana Sayfa")
}
},
label = { Text("Home") },
selected = false,
onClick = { }
)
}
BadgedBox, Badge'i otomatik olarak alt öğenin sağ üst köşesine konumlandırır. Sayı 99'u aşarsa, Badge "99+" görüntüler. Sıfır değerinde rozeti gizlemek için if (count > 0) kullanın. Material3'teki Badge varsayılan olarak beyaz metinle birlikte error rengine (kırmızı) sahiptir. BadgeDefaults.colors(containerColor, contentColor) ile özelleştirin.
Material2'de rozet yoktu — Canvas aracılığıyla manuel olarak çizilmesi veya üçüncü taraf kütüphaneler kullanılması gerekiyordu. Material3 bunu yerel bir bileşenle çözdü. Android Developers'a (2025) göre BadgedBox, NavigationBar kullanan uygulamaların %40'ında kullanılır ve bunların %60'ı okunmamış mesajları görüntüler.
Sıkça Sorulan Sorular
Material Design 3 ila 5 öğe önerir. Üçten az pratik değildir (bunun yerine Sekmeler kullanın). Beşten fazla, etiket okunabilirliğini ve dokunma hedef boyutunu azaltır. 5'ten fazla bölüm gerekiyorsa, Navigation Rail (tabletler) veya Drawer kullanın.
Çubuğun görünür olduğu rotaların bir listesini tanımlayın (genellikle kök rotalar). Scaffold bottomBar'da, yalnızca currentRoute bottomNavRoutes içindeyse NavigationBar'ı iletin. Bir alternatif, bottomBar olmadan iç içe ekranlar için ayrı bir grafiğe sahip NavHost'tur.
Material3'teki NavigationBar, Dinamik Renk, BadgedBox, WindowInsets ve animasyonlu aktif öğe genişliğini destekler. Material2'deki BottomNavigation, bu özellikler olmadan eski bir API'dir. Google, yeni projeler için NavigationBar kullanılmasını ve mevcut projelerin taşınmasını önerir.
Sorun, navigate() içinde saveState ve restoreState eksikliğidir. Bunlar olmadan NavController, önceki sekmenin durumunu kaydetmez. LazyColumn, kaydırma konumu ve giriş alanlarını korumak için NavOptions'a popUpTo(startDestination) { saveState = true } ve restoreState = true ekleyin.
Geçiş animasyonu, BottomNavigation değil, NavHost düzeyinde kontrol edilir. Özel animasyonlarla composable(route, enterTransition, exitTransition) ekleyin. BottomNavigation için 300 ms süreli solma animasyonu (fadeIn + fadeOut) tipiktir.
Özet
Anahtar teslim bir mobil uygulama geliştireceğiz
IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.
Ayrıca okuyun