So I had such UI structure at app top level composable (Scaffold to add bottom bar)
Scaffold(
contentWindowInsets = WindowInsets(0, 0, 0, 0),
bottomBar = {
if (appState.shouldShowBottomBarAndDrawer) {
AppBottomBar(
currentAppDestination = appState.currentDestination,
onTopLevelScreenNavigate = ...
)
}
}
) { innerPadding ->
AppNavGraph(
modifier = Modifier
.fillMaxSize()
.padding(innerPadding)
.consumeWindowInsets(innerPadding)
startDestination = appStartScreen,
navController = appState.navController,
modifier = Modifier
)
}
Each screen has its own Scaffold to add top bars with different actions and so on, so we can't really move such things to top level composable
The problem using this approach is when I switched from NavHost
to AnimatedNavHost
for animations it works ugly when you navigate up from screen without bottom bar to the screen with bottom (top level destination), so basically during animations we can see both screens and it adds navigation bar empty padding for screen without bottom bar during animation because appState.shouldShowBottomBarAndDrawer
returns true
now and each screen has that bottom padding for navigation bar, so content goes up, screen animation transition looks ugly.
But also there is different issue, not just animations, the vertical scroll jumps (for screens with lists or just scrollable) when state of bottom bar changes.
So there are minimum two issues with using Scaffold and bottom bar at top level destination - animations and scroll is not restored to the correct previous position when navigating up, it shifts every time
I decided to fix it the next way (replace Scaffold
to Box
for top level composable):
Box(modifier = Modifier.fillMaxSize()) {
AppNavGraph(
startDestination = appStartScreen,
navController = appState.navController,
modifier = Modifier.fillMaxSize()
)
Box(modifier = Modifier.align(Alignment.BottomCenter)) {
AppBottomBar(
currentAppDestination = appState.currentDestination,
onTopLevelScreenNavigate = ...
)
}
}
Basically keep NavHost
always with the same size, so it would not depend on bottom bar visibility state
But now I need to add bottom navigation bar padding for top level destination screens, I tried adding .padding(WindowInsets.navigationBars.asPaddingValues())
and .navigationBarsPadding()
but it didn't add any padding:
@Composable
fun DashboardScreen( // top level destination, the bottom bar is visible
...
) {
Scaffold { innerPadding ->
Box(
modifier = Modifier
.padding(innerPadding)
.consumeWindowInsets(innerPadding)
.padding(WindowInsets.navigationBars.asPaddingValues())
.navigationBarsPadding()
.fillMaxSize()
) {
// screen content
}
}
}
Right now the only solution I can use is to add additional custom bottom bar padding for each top level destination screen's:
@Composable
fun DashboardScreen( // top level destination, the bottom bar is visible
...
) {
Scaffold { innerPadding ->
Column(
modifier = Modifier
.padding(innerPadding)
.consumeWindowInsets(innerPadding)
.fillMaxSize()
.verticalScroll(rememberScrollState())
) {
// Screen Content
Spacer(modifier = Modifier.height(BottomBarContainerHeight))
}
}
}
BottomBarContainerHeight:
val BottomBarContainerHeight = 80.0.dp // Material3's NavigationBar's height
It's ugly but at least I don't have issues with transition animations and scroll right now because NavHost
's size is always the same
Would be grateful if somebody would show a better approach