simarjot singh kalsi
simarjot singh kalsi

Reputation: 411

Right strategy of using Bottom Navigation bar with Jetpack Compose

The official documentation of Compose Navigation component suggests that we should create a Navigation component by adding NavHost inside a Scaffold.

see https://developer.android.com/jetpack/compose/navigation#bottom-nav

The problem that i have with this approach is that with this approach the bottom nav bar will be visible in all the screens. I want it to be visible in just the screens that are present in the bottom bar items.

Second approach of adding bottom bar would be to not add NavHost inside Scaffold. Instead Add Bottom Bar in one of theDestinations, then hide/show Composables from the body of the Scaffold based on the index selected like this.

var selectedBottomBarIndex by remember {
    mutableStateOf(0)
}

Scaffold(
    bottomBar = {
            MyBottomBar(currentIndex = selectedBottomBarIndex, onChanged = {
                    selectedBottomBarIndex = it
                }
            )
    } {
    when (selectedBottomBarIndex) {        
        0 -> HomeScreen(navController)
        1 -> AssignmentHomeScreen(navController)
        2 -> CategoryScreen(navController)
        else -> ProfileHomeScreen(navController)
    }
}

I just want to know what are pros and cons of both the ways of adding bottom navigation. and is the second approach safe to use in a production application.

Upvotes: 2

Views: 4703

Answers (2)

CodeToLife
CodeToLife

Reputation: 4171

Copypast and play in 1 file Tabs.kt:

package com.example.navigationtest

import android.annotation.SuppressLint
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.material.BottomNavigation
import androidx.compose.material.BottomNavigationItem
import androidx.compose.material.Icon
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavDestination.Companion.hierarchy
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController

data class Profile(val id:String = "user for Profile")
data class Friends (val id:String = "user for Friends")
@Composable
fun FriendsScreen() {
    Column{
        Text(" FriendsScreen")
        Text("World Hello !")
    }
}
@Composable
fun ProfileScreen() {
    Column{
       Text("ProfileScreen")
        Text("Hello World!")
    }
}
data class TopLevelRoute<String>(val name: String, val route: String, val 
  icon: ImageVector)
class Icons {
    companion object {
        val Profile: Int = R.drawable.route_fridge
        val Friends: Int = R.drawable.map_pin_blue
    }
}
@SuppressLint("RestrictedApi")
@Composable
fun Tabs (){
    val topLevelRoutes = listOf(
        TopLevelRoute("MyProfile", "Profile", 
    ImageVector.vectorResource(Icons.Profile)),
    TopLevelRoute("MyFriends", "Friends", 
    ImageVector.vectorResource(Icons.Friends)
    )
)
val navController = rememberNavController()
Scaffold(
    bottomBar = {
        BottomNavigation(modifier = Modifier
            .height(70.dp),
            backgroundColor = Color(0xffdddddd)
        ) {
            val navBackStackEntry by navController.currentBackStackEntryAsState()
            val currentDestination = navBackStackEntry?.destination
            topLevelRoutes.forEach  { topLevelRoute ->
                val isSelected=currentDestination?.hierarchy?.any {
                    it.hasRoute(topLevelRoute.route,null)
                }==true
                BottomNavigationItem(
                    icon = { Icon(topLevelRoute.icon, contentDescription = topLevelRoute.name) },
                    label = { Text(topLevelRoute.name) },
                    selected = isSelected ,
                    onClick = {
                        navController.navigate(topLevelRoute.route) {
                            popUpTo(navController.graph.findStartDestination().id) {saveState = true}
                            launchSingleTop = true
                            restoreState = true
                        }
                    },
                    selectedContentColor = Color. Blue,
                   modifier = if(isSelected)
                        Modifier.background(Color(0xffdddddd))
                    else
                        Modifier.background(Color.Gray),
                    unselectedContentColor =  Color.White,

                )
            }
        }
    }
) { innerPadding ->
    NavHost(navController, startDestination = "Profile", Modifier.padding(innerPadding)) {
        composable(route="Profile") { ProfileScreen() }
        composable(route="Friends") { FriendsScreen() }
    }
}
}

and in Activity's onCreate() :

setContent(Tabs())

Upvotes: 0

Chirag Thummar
Chirag Thummar

Reputation: 3242

Here is an example of how to achieve it.

@Composable
fun JourneyApp() {
    JourneyTheme {
        val navController = rememberNavController()
        val onBack: () -> Unit = {
            navController.popBackStack()
        }
        val screens = listOf(
            Screen.Home,
            Screen.Collections,
            Screen.UserProfile
        )
        val showBottomBar = navController
            .currentBackStackEntryAsState().value?.destination?.route in screens.map { it.route }
        // https://developer.android.com/jetpack/compose/navigation
        Scaffold(
            bottomBar = {
                if (showBottomBar) {
                    BottomNavigation {
                        val navBackStackEntry by navController.currentBackStackEntryAsState()
                        val currentDestination = navBackStackEntry?.destination
                        screens.forEach { screen ->
                            BottomNavigationItem(
                                icon = { Icon(imageVector = screen.icon, contentDescription = screen.label) },
                                label = { Text(screen.label) },
                                selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true,
                                onClick = {
                                    navController.navigate(screen.route) {
                                        // Pop up to the start destination of the graph to
                                        // avoid building up a large stack of destinations
                                        // on the back stack as users select items
                                        popUpTo(navController.graph.findStartDestination().id) {
                                            saveState = true
                                        }
                                        // Avoid multiple copies of the same destination when
                                        // reselecting the same item
                                        launchSingleTop = true
                                        // Restore state when reselecting a previously selected item
                                        restoreState = true
                                    }
                                }
                            )
                        }
                    }
                }
            }
        ) { innerPadding ->
            Box(modifier = Modifier.padding(innerPadding)) {
                // nav graph
                NavHost(navController = navController, startDestination = Screen.Home.route) {
                    // add new navigation function
                    composable(Screen.Home.route) { HomeScreen(navigateToImportPhoto = { navController.navigate("importPhoto") }) }
                    composable(Screen.Collections.route) { CollectionsScreen() }
                    composable(Screen.UserProfile.route) { UserProfileScreen() }
                    // new screen, not part of bottom nav
                    composable("importPhoto") { ImportPhotoScreen(onBack = onBack) }
                }
            }
        }
    }
}

Upvotes: 6

Related Questions