I’m building a mobile app with Jetpack Compose and would like to implement bottom navigation with multiple tabs. I have a DashboardCustomerScreen that includes a bottom navigation bar with icons and titles for “Home,” “Reservation,” and “Profile.” I’m using a combination of Scaffold and custom components, and I have a working navigation setup using the Navigation component.
Here’s a snippet of the code for the DashboardCustomerScreen:
@Composable
fun DashboardCustomerScreen(profileCustomerViewModel: ProfileCustomerViewModel, dashboardViewModel: DashboardViewModel, navController: NavController) {
// ... (other code)
Scaffold(
topBar = {
// ... (top app bar)
},
bottomBar = {
// ... (bottom navigation bar)
},
content = {
// ... (content area)
}
)
}
I’m looking for guidance on refining the bottom navigation bar, handling item selection, and navigating to different destinations when a tab is selected. Are there any best practices or improvements I can make in terms of Jetpack Compose conventions?
Handle Item Selection:
Utilize the NavHost for handling navigation between different destinations. It looks like you already have a NavHost set up in the SetupNavGraph function. Ensure that each screen is properly annotated with @Composable and connected to the NavHost.
@RequiresApi(Build.VERSION_CODES.O)
@Composable
fun SetupNavGraph(
navController: NavHostController
){
NavHost(
navController = navController,
startDestination = Screen.Home.route
){
composable(Screen.Home.route){
Home(navController)
}
composable(Screen.DashboardCustomer.route){
DashboardCustomerScreen(ProfileCustomerViewModel(LocalContext.current, DataManager(LocalContext.current)),
DashboardViewModel(DataManager(LocalContext.current)), navController)
}
}
}
sealed class Screen(val route: String) {
object Home : Screen("home")
object DashboardCustomer : Screen("dashboardcustomer")
}
this is example of how i define bottom navigation
data class BottomNavigationItem(
val title: String,
val selectedIcon: ImageVector,
val unselectedIcon: ImageVector,
)
@RequiresApi(Build.VERSION_CODES.O)
@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class)
@Composable
fun DashboardCustomerScreen(profileCustomerViewModel: ProfileCustomerViewModel, dashboardViewModel: DashboardViewModel, navController: NavController) {
val items = listOf(
BottomNavigationItem(
title = "Home",
selectedIcon = Icons.Filled.Home,
unselectedIcon = Icons.Outlined.Home,
),
BottomNavigationItem(
title = "Reservation",
selectedIcon = Icons.Filled.StickyNote2,
unselectedIcon = Icons.Outlined.StickyNote2
),
BottomNavigationItem(
title = "Profile",
selectedIcon = Icons.Filled.Person,
unselectedIcon = Icons.Outlined.Person,
)
)
var selectedItemIndex by rememberSaveable { mutableStateOf(0) }
var expanded by remember {
mutableStateOf(false)
}
val isSuccessful by dashboardViewModel.isSuccessful.observeAsState()
if(isSuccessful == true){
navController.navigate("login")
}
var openDialog by remember{ mutableStateOf(false) }
if(openDialog){
ChangePasswordDialog(dashboardViewModel = dashboardViewModel, setShowDialog = { openDialog = it }, navController = navController)
}
Scaffold(
topBar = {
CenterAlignedTopAppBar(
colors = TopAppBarDefaults.centerAlignedTopAppBarColors(
containerColor = MaterialTheme.colorScheme.primaryContainer,
titleContentColor = MaterialTheme.colorScheme.primary,
),
title = {
Text(
"",
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
},
actions = {
IconButton(onClick = { expanded = true }) {
Icon(
imageVector = Icons.Filled.Menu,
contentDescription = "Localized description"
)
}
DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
) {
DropdownMenuItem(
text = {
Text(text = "Change Password")
},
onClick = {
expanded = false
openDialog = true
}
)
DropdownMenuItem(
text = {
Text(text = "Logout")
},
onClick = {
expanded = false
dashboardViewModel.logout()
}
)
}
},
)
},
bottomBar = {
NavigationBar() {
items.forEachIndexed { index, item ->
NavigationBarItem(
selected = selectedItemIndex == index,
onClick = {
selectedItemIndex = index
// when (index) {
// 0 -> navController.navigate("dashboard")
// 1 -> navController.navigate("login")
// }
},
label = {
Text(text = item.title)
},
icon = {
Icon(
imageVector =
if (selectedItemIndex == index)
item.selectedIcon
else item.unselectedIcon,
contentDescription = item.title
)
}
)
}
}
},
content = {
padding ->
Row(
Modifier
.fillMaxSize()
.padding(padding)
.consumeWindowInsets(padding)
.windowInsetsPadding(
WindowInsets.safeDrawing.only(
WindowInsetsSides.Horizontal,
),
),
) {
Column(Modifier.fillMaxSize()
)
{
when (selectedItemIndex) {
0 -> ReservationScreen(
ReservationViewModel(
DataManager(LocalContext.current)
)
)
1 -> ReservasiScreen(
ReservasiViewModel(
LocalContext.current,
DataManager(LocalContext.current)
),
ReservasiUangJaminanViewModel(
DataManager(LocalContext.current),
LocalContext.current
),
navController
)
2 -> ProfileCustomerScreen(profileCustomerViewModel, navController)
}
}
}
}
)
}
You can integrate/refer this library to create stylish navigation bar(Checkout sample app): github.com/canopas/compose-animated-navigationbar