I'm doing this post because I'm triying to create an application that has the following navigation:
GlobalNavigation
│
├── AuthActivity (without bottombar)
│ ├── LoginScreen
│ ├── RecoveryScreen
│ ├── SignUpScreen
│
└── MainActivity
│
├── HomeNavTopLevel Graph (Home, Chat, Settings…) → with BottomBar
│ ├ Home
│ ├ Profile
│
└── SettingsNavTopLevel Graph (Setting or more screens) → without BottomBar
├ One or more screens
My question is the next:
How to avoid the SettingsScreen resize when I navigate to it from a bottom bar screen?
I have to create a NavDisplay for every top level to avoid the bottombar at the settings screeen (even if settings isn't part of the bottom bar).
My current implementation is one NavDisplay inside a Scaffold holding the MainActivity navigation like the example above, hiding the bottomBar when the topLevelStack is not a BottomBar interface, but if you the gif in the bottom you will see that when I navigate to Settings, the bottom bar is hide but the SettingsScreen (Yellow) for a moment has the same height of the bottom bar screens and a few miliseconds (or seconds) it expands along the screen (because the bottom bar was hide).
NOTE: In navigation2 I created a navController for each sub-navigation, like auth and main has a NavHostController but inside of it a create subNavigation with Navigation and just for Home I created another NavHostController to manage the BottomBar navigation.
MainActivity content (Composables):
@Composable
fun MainContent(
openAuthActivity: () -> Unit,
navigator: MainNavigator
) {
SerenoTheme(
dynamicColor = false
) {
Scaffold(
modifier = Modifier
.fillMaxSize(),
bottomBar = {
AnimatedVisibility(
navigator.topLevelKey is BottomBar,
enter = fadeIn(),
exit = fadeOut()
) {
NavigationBar(
) {
HomeBottomBarItems.HOME_LEVEL_ROUTES.forEach { (key, item) ->
val isSelected = key == navigator.topLevelKey
NavigationBarItem(
selected = isSelected,
onClick = {
navigator.switchTopLevel(key)
},
icon = {
Icon(
imageVector = item.icon,
contentDescription = null,
)
},
label = {
Text(
text = stringResource(item.labelStringRes)
)
}
)
}
}
}
}
) { innerPadding ->
val modifier = remember {
Modifier.padding(innerPadding)
}
MainDisplay(
modifier = modifier,
navigator = navigator,
openAuthActivity = openAuthActivity
)
}
}
}
Mi sealed interface for Destinations:
sealed interface BottomBar
sealed interface MainRoute : NavKey {
@Serializable
data object Home : MainRoute, BottomBar
@Serializable
data object Profile : MainRoute, BottomBar
@Serializable
data object Settings : MainRoute
}
AppNavigator to support multiple back stacks:
class AppNavigator(private val startKey: T) {
private var topLevelBackStacks: HashMap> = hashMapOf(
startKey to mutableStateListOf(startKey)
)
var topLevelKey by mutableStateOf(startKey)
private set
val backStack = mutableStateListOf(startKey)
private fun updateBackStack() {
backStack.clear()
val currentStack = topLevelBackStacks[topLevelKey] ?: emptyList()
if (topLevelKey == startKey) {
backStack.addAll(currentStack)
} else {
val startStack = topLevelBackStacks[startKey] ?: emptyList()
backStack.addAll(startStack + currentStack)
}
}
fun switchTopLevel(key: T) {
if (topLevelBackStacks[key] == null) {
topLevelBackStacks[key] = mutableStateListOf(key)
}
topLevelKey = key
updateBackStack()
}
fun add(key: T) {
topLevelBackStacks[topLevelKey]?.add(key)
updateBackStack()
}
fun removeLast() {
val currentStack = topLevelBackStacks[topLevelKey] ?: return
if (currentStack.size > 1) {
currentStack.removeLastOrNull()
} else if (topLevelKey != startKey) {
topLevelKey = startKey
}
updateBackStack()
}
fun replaceStack(vararg keys: T) {
topLevelBackStacks[topLevelKey] = mutableStateListOf(*keys)
updateBackStack()
}
}
And a video showing how the application works:
