i have a Jetpack Compose project that includes navigating using a Scaffold
with a BottomBar
. By the way, in specific screens, i would like to being able to navigate outside of the innerPadding
scope of the Scaffold
and just have the composable to be displayed in full screen.
Using official docs approach is fine only until you just need to navigate clicking on the items of the BottomBar
.
I found a workaround solution in this post where you use this:
@Composable
fun OneScreen(navController: NavHostController) {
MainScaffold(
bottomBar = { BottomBar(navController = navController) },
content = {
// content
})
}
for screens where BottomBar
needs to be displayed and this:
@Composable
fun AnotherScreen() {
MainScaffold () {
//content
}
}
for the screens where you don't need it to be diplayed.
I call this a "workaround solution" because in this way you need to regenerate the BottomBar
on every screen, making it flashing everytime you click on one item because of recomposition (differently from standard BottomBar navigation).
I'm getting the feeling that there should be a more elegant solution, but i lack the experience to be sure of it and i have not been able to find it until now.
EDIT: I was WRONG on a relevant aspect. The flashing is not because of recomposition but because of the Transition Animation
(Fade out/Fade in) provided by the Navigation
component. Hence, it is possible to eliminate the 'flashing' issue setting No animation on transition. Of course this is still a work-around solution because it limit the user on the flexibility in animating the app.
I found a workaround that i believe to be better than the one linked in the previous post. I decided to use the following approach:
NavGraph
navigation for all the Screens
that don't include BottomBar
BottomBar
Screen itemsIn this way i think it is easier to manage the Transition Animations
and having readable code.
I'm sure this is not the best solution (and i'm not going to mark this as solution) but i think it's worth sharing the approach.
MainActivity
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyBottomNavTestTheme {
val navController = rememberNavController()
SetupNavGraph(navController)
}
}
}
}
NavGraph
const val TEST_ROUTE = "test_route"
@Composable
fun SetupNavGraph(navController: NavHostController) {
NavHost(
navController = navController,
startDestination = Screen.MainScreen.route,
route = TEST_ROUTE
) {
composable(Screen.MainScreen.route) { MainScreen(navController) }
composable(Screen.AnotherScreen.route) { AnotherScreen(navController) }
}
}
MainScreen
@Composable
fun MainScreen(navController: NavHostController){
var selectedItem by remember { mutableStateOf(0)}
Scaffold(
bottomBar = { MyBottomNavBar() {selectedItem = it} }
) {
selectedItem ->
when (selectedItem){
0 -> TabOne(navController)
1 -> TabTwo(navController)
2 -> TabThree(navController)
}
}
}
@Composable
fun MyBottomNavBar(
onSelectedItem: (Int) -> Unit
) {
BottomNavigation() {
BottomNavigationItem(
selected = true,
onClick = { onSelectedItem(0) },
icon = { Icon(imageVector = Icons.Filled.Person, contentDescription = "Person Icon") },
enabled = true,
)
BottomNavigationItem(
selected = true,
onClick = { onSelectedItem(1) },
icon = { Icon(imageVector = Icons.Filled.Phone, contentDescription = "Phone Icon") },
enabled = true,
)
BottomNavigationItem(
selected = true,
onClick = { onSelectedItem(2) },
icon = { Icon(imageVector = Icons.Filled.Place, contentDescription = "Place Icon") },
enabled = true,
)
}
}
@Composable
fun TabOne(navController: NavHostController){
Surface(
modifier = Modifier
.fillMaxSize()
){
Text(
text = "TabOne"
)
}
}
@Composable
fun TabTwo(navController: NavHostController){
Surface(
modifier = Modifier
.fillMaxSize()
){
Text(
text = "TabTwo"
)
}
}
@Composable
fun TabThree(navController: NavHostController){
Surface(
modifier = Modifier
.fillMaxSize()
){
Column(){
Text(
text = "TabThree"
)
Button(onClick = {navController.navigate(Screen.AnotherScreen.route)}){
Text("Go to AnotherScreen")
}
}
}
}
AnotherScreen
@Composable
fun AnotherScreen(navController: NavHostController){
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Button(onClick = {navController.navigate(Screen.MainScreen.route)}) {
Text("Go to MainScreen")
}
}
}