Billy Newman
06/09/2022, 4:59 PMViewModelStore should be set before setGraph call
Code in comments@Composable
fun MainScreen() {
val mainNavController = rememberNavController()
val homeNavController = rememberNavController()
val scaffoldState = rememberScaffoldState()
NavHost(
navController = mainNavController,
startDestination = "main"
) {
composable("main") {
Box(Modifier.fillMaxSize()) {
TextButton(
modifier = Modifier.align(Alignment.Center),
onClick = { mainNavController.navigate("home") }
) {
Text("Home")
}
}
}
composable("home") {
Scaffold(
scaffoldState = scaffoldState,
bottomBar = {
BottomNavigation {
val navBackStackEntry by homeNavController.currentBackStackEntryAsState()
val currentDestination = navBackStackEntry?.destination
BottomNavigationItem(
icon = {
Icon(
Icons.Default.Share,
contentDescription = "Tab 1"
)
},
label = { Text("Tab 1") },
selected = currentDestination?.hierarchy?.any { it.route == "home/tab1" } == true,
onClick = {
homeNavController.navigate("home/tab1") {
popUpTo(homeNavController.graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
}
)
BottomNavigationItem(
icon = {
Icon(
<http://Icons.Default.Menu|Icons.Default.Menu>,
contentDescription = "Tab 2"
)
},
label = { Text("Tab 1") },
selected = currentDestination?.hierarchy?.any { it.route == "home/tab2" } == true,
onClick = {
homeNavController.navigate("home/tab2") {
popUpTo(homeNavController.graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
}
)
}
}
) {
NavHost(
navController = homeNavController,
startDestination = "home/tab1"
) {
composable("home/tab1") {
Box(
Modifier
.fillMaxSize()) {
Column( modifier = Modifier.align(Alignment.Center)) {
Text("Tab 1")
TextButton(
onClick = {
mainNavController.navigate("main")
homeNavController.navigate("home/tab1")
})
{
Text("Main")
}
}
}
}
composable("home/tab2") {
Box(
modifier = Modifier.fillMaxSize(),
) {
Column( modifier = Modifier.align(Alignment.Center)) {
Text("Tab 2")
TextButton(
onClick = {
mainNavController.navigate("main")
})
{
Text("Main")
}
}
}
}
}
}
}
}
}
Colton Idle
06/09/2022, 5:37 PMBilly Newman
06/09/2022, 5:40 PMColton Idle
06/09/2022, 5:49 PMwhich I don’t think is wrong just what most are used to,I don't want to relay the wrong information, but I could've sworn Ian has said that that's the wrong thing to do. Maybe he'll chime in here. I have bottom bar enter/exit animated. let me send you a snippet.
AnimatedVisibility(
currentDestination?.hierarchy?.any {
it.route == Screen.Tab1.route ||
it.route == Screen.Tab2.route ||
it.route == Screen.Tab3.route
} == true,
enter =
slideInVertically(
initialOffsetY = { fullHeight: Int -> fullHeight }, animationSpec = tween(950)) +
fadeIn(animationSpec = tween(950)),
exit = fadeOut(animationSpec = tween(650)),
) {
Sterling Albury
06/09/2022, 5:52 PMBilly Newman
06/09/2022, 6:01 PMColton Idle
06/09/2022, 6:04 PMBilly Newman
06/09/2022, 6:06 PMColton Idle
06/09/2022, 6:10 PMIan Lake
06/09/2022, 6:25 PMFor me the part that feels weird is to have to define a scaffold which might include a bottom sheet, a drawer and/or a bottom bar at the top level of the entire application. Considering that half of your app might not need those and just hide them, relying on other routes to show them. Would be nice to be able to use a scaffold at lower levels but still hook into the existing navController.If you want global UI, then putting it at the global level is exactly what you need to do - remember a `NavHost`/`AnimatedNavHost` is just a wrapper around an
AnimatedContent
composable, swapping between screens. You still need to figure out what needs to go inside and outside that level, whether you are using Navigation Compose or notBilly Newman
06/09/2022, 6:41 PMYou still need to figure out what needs to go inside and outside that level, whether you are using Navigation Compose or notAm I correct to assume that you are saying “put the scaffold/bottombar at the level you need it”. This was the intent of my original question, can I use a navhost to navigate to screens that contain a bottom sheet navigation component. I “hacked” in 2 NavHosts and it worked, however I would like to follow best practices.
fun MainScreen() {
val bottomSheetNavigator = rememberBottomSheetNavigator()
val navController = rememberNavController(bottomSheetNavigator)
NavHost(
navController = navController,
startDestination = "main"
) {
homeGraph(navController, bottomSheetNavigator)
fun NavGraphBuilder.homeGraph(
navController: NavController,
bottomSheetNavigator: BottomSheetNavigator
) {
composable("home") {
val scaffoldState = rememberScaffoldState()
ModalBottomSheetLayout(bottomSheetNavigator) {
Scaffold(
scaffoldState = scaffoldState,
drawerContent = {...},
bottomBar = {...}
) {
// Not entirely sure how to setup bottom nav tabs within the scaffold?
}
}
}
}
Ian Lake
06/10/2022, 2:09 AMModalBottomSheetLayout
within a composable
destination. That is absolutely something that should exist at the global level (and needs to be if you consider contentPadding, system insets, and everything else that forces a ModalBottomSheetLayout
to need to be at the topmost level). That will always be the case until the bottom sheet APIs are changed completelyDialog
is something being discussed in https://issuetracker.google.com/issues/217840726, which would potentially remove the need to use something like ModalBottomSheetLayout
at all. I'd definitely +1 that CL is that's something you'd find easier to work withSterling Albury
06/10/2022, 2:16 AMIan Lake
06/10/2022, 3:04 AMBilly Newman
06/10/2022, 3:09 AMIan Lake
06/10/2022, 4:31 AMcomposable
that adds a shared element bottom bar to each composable destination i.e.:
fun NavGraphBuilder.bottomNavWrappedComposable(
navController: NavController,
// all the normal parameters to composable including the content block:
content: @Composable (NavBackStackEntry) -> Unit
) = composable(
// Add in all of the normal parameters
) { entry ->
// Scaffold uses an expensive SubcomposeLayout, you really just need a Column
Column {
// First, display the content in a Box
Box(modifier = Modifier.weight(1f) {
content(entry)
}
// Then put your BottomNavigationBar, using the same logic as
// the docs use: <https://developer.android.com/jetpack/compose/navigation#bottom-nav>
BottomNavigationBar(
// This would be the secret sauce that would keep the bottom bar in place as
// other elements transition as you move between screens
modifier = Modifier.sharedElement("bottomBar")
) {
// your items
}
}
}
Essentially, your wrapper provides common UI, using shared elements to keep some elements in place as you move between screensdorche
06/10/2022, 11:45 AMScaffold(
bottomBar = {
AnimatedVisibility(
visible = showBottomNav,
enter = slideInVertically(initialOffsetY = { it }),
exit = slideOutVertically(targetOffsetY = { it }),
) {
bottomBar(destination)
},
and I was getting a quite visible layout jump whenever you navigate from a screen that has the BottomBar showing to one that doesn’t. I was putting that to the fact that AnimateVisibility doesn’t animate the height but now I’m thinking that’s wrong? Does it work smoothly for you, with the “content” nicely expanding to take the space freed up by the bottom bar?Colton Idle
06/10/2022, 11:58 AMdorche
06/10/2022, 12:00 PM