Jetpack Compose Bottom Navigation With Scaffold (Material3)
What is a Scaffold?
In Material Design, a scaffold is a fundamental structure that provides a standardized platform for complex user interfaces. It holds together different parts of the UI, such as app bars and floating action buttons, and the bottom bar gives apps a coherent look and feel.
Scaffold
accepts several composables as parameters. Among these are the following:
topBar
: The app bar is across the top of the screen.bottomBar
: The app bar is across the bottom of the screen.floatingActionButton
: A button that hovers over the bottom-right corner of the screen that you can use to expose key actions.
In this example, we will include topBar
and bottomBar
with the complete example.
Let’s start it
I hope you have set up the latest libs and material3 with the latest Android studio version. We also include one case where you want to hide the bottom bar.
Define Main Screen NavHost
which will be responsible for showing Main Screens Home And Settings.
@Composable
fun BottomNavWithScaffold(
) {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = NavRoute.Home.route,
modifier = Modifier.fillMaxSize()
) {
composable(route = NavRoute.Home.route) {
HomeScreen() {
navController.navigate(it)
}
}
composable(route = NavRoute.Settings.route) {
SettingsScreen()
}
}
}
Now, we will make another NavHost
which contains the BottomBar
and three screen with Scaffold
.
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun HomeScreen(
navigateTo: (route: String) -> Unit
) {
val topLevelDestinations = listOf(
TopLevelDestination(
route = NavRoute.Screen1.route,
selectedIcon = R.drawable.home,
unselectedIcon = R.drawable.home_outline,
iconText = "Home"
), TopLevelDestination(
route = NavRoute.Screen2.route,
selectedIcon = R.drawable.bulb,
unselectedIcon = R.drawable.bulb_outline,
iconText = "Generate"
), TopLevelDestination(
route = NavRoute.Screen3.route,
selectedIcon = R.drawable.my_files,
unselectedIcon = R.drawable.my_files_outline,
iconText = "My Files"
)
)
val showBottomBar = remember { mutableStateOf(true) }
val title = remember {
mutableStateOf("Home")
}
val navController = rememberNavController()
Scaffold(
topBar = {
CenterAlignedTopAppBar(title = {
Text(
text = title.value,
color = Color.White,
fontWeight = FontWeight.Bold,
fontSize = 20.sp,
)
}, colors = TopAppBarDefaults.centerAlignedTopAppBarColors(
containerColor = MaterialTheme.colorScheme.primary
), actions = {
Icon(
imageVector = Icons.Filled.Settings,
tint = MaterialTheme.colorScheme.onPrimary,
contentDescription = "Settings",
modifier = Modifier
.clickable {
navigateTo(NavRoute.Settings.route)
}
.padding(8.dp)
)
})
},
bottomBar = {
if (showBottomBar.value) {
HomeBottomBar(destinations = topLevelDestinations,
currentDestination = navController.currentBackStackEntryAsState().value?.destination,
onNavigateToDestination = {
title.value = when (it) {
"sc1" -> "Screen 1"
"sc2" -> "Screen 2"
else -> {
"Screen 3"
}
}
navController.navigate(it) {
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
restoreState = true
launchSingleTop = true
}
})
}
}
) {
Column(
modifier = Modifier
.padding(it)
.fillMaxSize()
) {
HomeNavHost(
modifier = Modifier
.fillMaxSize()
.weight(1f),
navController = navController,
startDestination = NavRoute.Screen1.route,
navigateTo = navigateTo
)
}
}
}
topLevelDestinations
: We have made one data class that will hold the icons, text and route for BottomNav.Scaffold
: It is responsible for holding all the UI of the screen together.CenterAlignedTopAppBar
: This is responsible for showing the Title, Navigation Icon, Menu, etc.HomeNavHost
: This will hold all three screens and also change the TopAppBar title when we switch the screen using a bottom bar.
@Composable
fun HomeNavHost(
modifier: Modifier,
navController: NavHostController,
startDestination: String,
navigateTo: (route: String) -> Unit
) {
NavHost(
navController = navController, startDestination = startDestination, modifier = modifier
) {
composable(route = NavRoute.Screen1.route) {
Screen1()
}
composable(route = NavRoute.Screen2.route) {
Screen2()
}
composable(route = NavRoute.Screen3.route) {
Screen3()
}
}
}
@Composable
fun Screen1() {
Column(modifier = Modifier.fillMaxSize()) {
Text(text = "One Screen", fontSize = 20.sp)
}
}
@Composable
fun Screen2() {
Column(modifier = Modifier.fillMaxSize()) {
Text(text = "Two Screen", fontSize = 20.sp)
}
}
@Composable
fun Screen3() {
Column(modifier = Modifier.fillMaxSize()) {
Text(text = "Three Screen", fontSize = 20.sp)
}
}
Sample Screen PlaceHolders
Bottombar Item Design
@Composable
private fun HomeBottomBar(
destinations: List<TopLevelDestination>,
currentDestination: NavDestination?,
onNavigateToDestination: (route: String) -> Unit
) {
NavigationBar(
modifier = Modifier
.windowInsetsPadding(
WindowInsets.safeDrawing.only(WindowInsetsSides.Horizontal + WindowInsetsSides.Bottom)
)
.height(70.dp),
) {
destinations.forEach { destination ->
val selected =
currentDestination?.hierarchy?.any { it.route == destination.route } == true
NavigationBarItem(
selected = selected,
onClick = { onNavigateToDestination(destination.route) },
icon = {
val icon = if (selected) {
destination.selectedIcon
} else {
destination.unselectedIcon
}
Icon(
imageVector = ImageVector.vectorResource(icon),
modifier = Modifier.size(16.dp),
contentDescription = null
)
},
label = {
Text(
text = destination.iconText
)
})
}
}
}
That’s it. We have made an app that contains the BottomNavigation with Different Screens And Navigation Best Practices.
🍴Check out the complete code on my GitHub Repository. ✍️ Hope this project really helps you.
Hope you enjoy coding Jetpack Compose 😁. Don’t forget to share 📨 and clap 👏.
Any Suggestions are welcome. If you need any help or have questions for Code Contact Me. You can follow me on LinkedIn, StackOverflow and Twitter For More Updates 🔔
Happy Compose !! 🚀
Preview