hi everybody! I don’t know if this has already bee...
# compose
m
hi everybody! I don’t know if this has already been asked around here, but here’s my question: I have a composable which is the one inside my main content, that has a scaffold with a top bar, a content and a bottom navigation bar. I’m creating more composables in the content part, and in this case I have a
LazyColumnFor
, and I’m also using the navigation for compose. My question is that when I click on an item from the composable that has the
LazyColumnFor
I’m navigating to the detail composable of that item (let’s call it
ItemDetail
) and here, I see that the content is between the top bar and the bottom nav bar of the scaffold of the parent component. Is it possible to have a reference to the top bar and the bottom nav bar? For example I want to add the back arrow to the top bar for the up navigation. Thanks in advance!
z
Your navhost is a child of the scaffold?
m
yes; it’s actually created inside the content of the bottomBar
I did it based on
Copy code
val navController = rememberNavController()
Scaffold(
    bottomBar = {
        BottomNavigation {
            val navBackStackEntry by navController.currentBackStackEntryAsState()
            val currentRoute = navBackStackEntry?.arguments?.getString(KEY_ROUTE)
            items.forEach { screen ->
                BottomNavigationItem(
                    icon = { Icon(Icons.Filled.Favorite) },
                    label = { Text(stringResource(screen.resourceId)) },
                    selected = currentRoute == screen.route,
                    onClick = {
                        // This is the equivalent to popUpTo the start destination
                        navController.popBackStack(navController.graph.startDestination, false)

                        // This if check gives us a "singleTop" behavior where we do not create a
                        // second instance of the composable if we are already on that destination
                        if (currentRoute != screen.route) {
                            navController.navigate(screen.route)
                        }
                    }
                )
            }
        }
    }
) {

    NavHost(navController, startDestination = Screen.Profile.route) {
        composable(Screen.Profile.route) { Profile(navController) }
        composable(Screen.FriendsList.route) { FriendsList(navController) }
    }
}
z
Unless I’m reading something wrong, it looks to me like your NavHost is in the main body slot of the scaffold, not the bottom bar
You can read the current back stack entry as state to figure out what additional ui to put in your top and bottom bars as well as using it for the bottom navigation
i
Yep, the whole reason you use
rememberNavController()
outside of the NavHost itself is to hook up other things to changes in the NavController
So just like how your bottom bar reacts to changes and calls
navigate()
, so can your top bar show/hide an arrow if you are on the start destination of your graph and hook that up to
navController.navigateUp()
a
Hi @Ian Lake, in relation to this.. doing something like this, is correct right?
Copy code
val navController = rememberNavController()
val navBackStackEntry by navController.currentBackStackEntryAsState()

Scaffold(
    bottomBar = {
        //----------- This right here -------//
        val isBottomBarVisible = navBackStackEntry?.arguments?.getString(MY_KEY) 
        //----------- This right here -------//

        if (isBottomBarVisible) {
            MyBottomBar()
        }
    }
) {
    NavHost(navController, startDestination = Screen.Profile.route) {  
        composable(Screen.Profile.route) { 
            Profile(navController) 
        }
        composable(
            Screen.MyScreen.route + "?${MY_KEY}=MY_KEY",
            ....
        ) { 
            MyScreen(navController) 
        }
    }
}
I figured I could also do something like this and achieve the same...
Copy code
val navController = rememberNavController()
val navBackStackEntry by navController.currentBackStackEntryAsState()
val isBottomBarVisible by rememeber { mutableStateOf(false) }

Scaffold(
    bottomBar = {
        if (isBottomBarVisible) {
            MyBottomBar()
        }
    }
) {
    NavHost(navController, startDestination = Screen.Profile.route) {  
        composable(Screen.Profile.route) { 
            Profile(navController) 
        }
        composable(
            Screen.MyScreen.route
        ) { 

            //----------- This right here -------//
            onCommit(navBackStackEntry) {
                isBottomBarVisible = true
            }
            //----------- This right here -------//

            MyScreen(navController) 
        }
    }
}
Was wondering if both approaches are valid?
i
Well, the former is using a single source of truth to automatically recompose your bottom nav in the same recomposition that the current destination changes and swaps from visible to not visible and back again while the later is building a whole other state that is set to true as a side effect of one destination being composed, forcing a second recomposition (since onCommit happens after the first composition finishes) and then never set back to false. So no, those aren't equivalent at all
a
I see... thank you very much!
m
thanks a lot guys!
one thing I don’t get is that
navController.navigateUp()
from this detail view returns false and doesn’t navigate back
ok I understood it now: I need to pass the navController to my composable, and that was I can make it work 😄
what about Crossfade? how do animations work with navigation in this case? is there any sample to take a look at?
a
@Ian Lake How about for things that couldn't be passed in as parameter? like Callbacks?
Copy code
val navController = rememberNavController()
var onCloseClick: (() -> Unit)? = null

Scaffold(
    topBar = {
        TopAppBar(
            ...,
             actions = {
                 IconButton(onClick = {
                     onCloseClick?.invoke()
                  }) {
                      Icon(asset = Icons.Default.Close)
                   }
             }
         )
     },
) {
    NavHost(
        navController = navController,
        startDestination = ScreenA.route,
    ) {
        composable(startingRoute) {
            // ------- This here ------- //
            onCommit(currentBackStackEntry) {
                onCloseClick = {
                    // Do something
                }
            }
            onDispose {
                onCloseClick = null
            }
        }
        composable(otherRouter) {
            // ------- This here ------- //
            onCommit(currentBackStackEntry) {
                onCloseClick = {
                    // Do something else
                }
            }
            onDispose {
                onCloseClick = null
            }
        }
    }
}
@Manuel Lorenzo, there is a thread where they are discussing about that but I can't find it.. ill link it here once i see it.
m
thanks man!
i
No animation support as of yet - the previous thread we discussed animations in was https://kotlinlang.slack.com/archives/CJLTWPH7S/p1604093221008800, the issue you can star is https://issuetracker.google.com/172112072
m
Thanks a lot for the info @Ian Lake and keep up the good work! 👏