The Material Design 3 NavigationBar and NavigationBarItem composables (or in Material Design 2, BottomNavigation and BottomNavigationItem) create a horizontal row of three to five icons and text labels that control overall screen contents.
These standard controls announce their name, role, and value in accordance with the WCAG Success Criterion 4.1.2 Name, Role, Value and correctly express their relationships according to WCAG Success Criterion 1.3.1 Info and Relationships. Custom approaches are likely to be less accessible; make sure any navigation bar item announces list semantics.
NavigationBar layouts operate like TabRow composables and are fully accessible by default. (NavigationBarItems even announce in TalkBack as "Tab".)
Take the following steps to implement a NavigationBar:
- Create a
NavHostControllerwithrememberNavController(). - Remember which navigation bar item is selected (and preserve that value across configuration changes).
- In the
ScaffoldbottomBar, compose aNavigationBarand populate its content withNavigationBarItems. - For each
NavigationBarItem:- Use
selectedto identify the currently selectedNavigationBarItem. - In
onClick, change the selected navigation bar item and perform screen navigation usingNavHostController.navigate()and a route string. - Use
iconto display an appropriate icon for the navigation bar item.- Use a filled icon for the selected navigation bar item, and an outlined icon otherwise.
- Best practice is to always specify an
Icon'scontentDescription; however, not that it will only be used when a text label is not present or not displayed.
- Use
labelto define the appropriate navigation bar item's text label.- Do not use
Text.maxLinesorModifier.heightto limit text display.
- Do not use
- Use
alwaysShowLabelto determine if the text label is displayed for unselected navigation bar items.- Prefer displaying a text label on all navigation bar items per Material Design guidelines.
- Best practice is to omit this property, letting it default to
true.
- Use
- In the
Scaffoldcontent, compose aNavHost, tie it to the rememberedNavHostControllerand define acomposablefor each navigation bar item by route string.
For example:
// Controls navigation by associating the NavigationBar with a NavHost. Perform navigation in each
// NavigationBarItems' onClick method by calling pageNavController.navigate() with a route string.
val pageNavController = rememberNavController()
// NavigationBarItem data
data class NavItemData(
val label: String,
@DrawableRes val selectedIcon: Int,
@DrawableRes val unselectedIcon: Int
)
val navItemsData = listOf(
NavItemData("Home", R.drawable.home_24dp_filled, R.drawable.home_24dp_outlined),
NavItemData("Favorites", R.drawable.favorite_24dp_filled, R.drawable.favorite_24dp_outlined),
NavItemData("Settings", R.drawable.settings_24dp_filled, R.drawable.settings_24dp_outlined),
)
Scaffold(
// ...
bottomBar = {
// Remember the selected navigation item across configuration changes.
var selectedItemIndex by rememberSaveable { mutableStateOf(0) }
// Place the NavigationBar in the Scaffold's bottomBar so it is locked to the screen bottom.
NavigationBar {
// Populate the NavigationBar content with NavigationBarItem composeables.
navItemsData.forEachIndexed { index, navItemData ->
NavigationBarItem(
// Indicate the selected NavigationBarItem
selected = selectedItemIndex == index,
// On click, change the selected NavigationBarItem and perform navigation
onClick = {
selectedItemIndex = index
pageNavController.navigate(navItemData.label)
},
// Display the appropriate icon. Use a filled icon for the selected item.
icon = {
Icon(
painter = painterResource (
if (selectedItemIndex == index)
navItemData.selectedIcon
else
navItemData.unselectedIcon
),
// Icon contentDescription will only be used if there is no label or the
// label is not shown. Best practice is to provide it.
contentDescription = navItemData.label
)
},
// Always provide a text label. Take care not to set maxLines or height limits.
label = { Text(navItemData.label) },
)
}
}
}
) { paddingValues ->
// Place the NavHost in the Scaffold's content.
NavHost(
navController = pageNavController,
startDestination = "Home",
modifier = Modifier.padding(paddingValues)
) {
composable("Home") {
// ... Home screen content
}
composable("Favorites") {
// ... Favorites screen content
}
composable("Settings") {
// ... Settings screen content
}
}
}(Note: The hard-coded text shown in these examples is only used for simplicity. Always use externalized string resource references in actual code.)
Copyright 2024-2025 CVS Health and/or one of its affiliates
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and limitations under the License.