How to arrange BottomNavigationItems in Compose?

1.9k views Asked by At

enter image description here

How can I arrange the two inner BottomNav Items so that they are not so close to the "+" FAB? I tried surrounding the forEach which displays the Items with a Row and use the Arrangement modifier like so:

Row(horizontalArrangement = Arrangement.SpaceBetween) { //Not working :(
items.forEach { item ->
    BottomNavigationItem(
        icon = { Icon(painterResource(id = item.icon), contentDescription = item.title) },
        label = { Text(text = item.title) },
        selectedContentColor = Color.White,
        unselectedContentColor = Color.White.copy(0.4f),
        alwaysShowLabel = true,
        selected = currentRoute == item.route,
        onClick = {
            navController.navigate(item.route) { 
                navController.graph.startDestinationRoute?.let { route ->
                    popUpTo(route) {
                        saveState = true
                    }
                } 
                launchSingleTop = true 
                restoreState = true
            }
        }
    )
}

}

Unfortunately thats not working

2

There are 2 answers

0
Phil Dukhov On BEST ANSWER

Arrangement.SpaceBetween works as expected - it adds a spacer between items:

Place children such that they are spaced evenly across the main axis, without free space before the first child or after the last child. Visually: 1##2##3

You need to let your Row know about FAB location. You can add a spacer with Modifier.weight in the middle of your row, for example like this:

items.forEachIndexed { i, item ->
    if (i == items.count() / 2) {
        Spacer(Modifier.weight(1f))
    }
    BottomNavigationItem(
        // ...
0
Mayur Gajra On

You can use BottomAppBar & give it cutoutShape with a dummy item in the middle. It would give you your desired results.

Output:

bottomnav fab

Code Sample:

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            AppTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colors.background
                ) {
                    BottomBarWithFabDem()
                }
            }
        }
    }
}

val items = listOf(
    Screen.PickUp,
    Screen.Explore,
    Screen.Camera,
    Screen.Favorites,
    Screen.Profile
)

sealed class Screen(val route: String?, val title: String?, val icon: ImageVector?) {
    object PickUp : Screen("pickup", "PickUp", Icons.Default.ShoppingCart)
    object Explore : Screen("explore", "Explore", Icons.Default.Info)
    object Camera : Screen("camera", null, null)
    object Favorites : Screen("favorites", "Fav", Icons.Default.Favorite)
    object Profile : Screen("profile", "Profile", Icons.Default.Person)
}

@Composable
fun BottomBarWithFabDem() {
    val navController = rememberNavController()
    Scaffold(
        bottomBar = {
            BottomNav(navController)
        },
        floatingActionButtonPosition = FabPosition.Center,
        isFloatingActionButtonDocked = true,
        floatingActionButton = {
            FloatingActionButton(
                shape = CircleShape,
                onClick = {
                    Screen.Camera.route?.let {
                        navController.navigate(it) {
                            popUpTo(navController.graph.findStartDestination().id) {
                                saveState = true
                            }
                            launchSingleTop = true
                            restoreState = true
                        }
                    }
                    Screen.Camera.route?.let { navController.navigate(it) }
                },
                contentColor = Color.White
            ) {
                Icon(imageVector = Icons.Filled.Add, contentDescription = "Add icon")
            }
        }
    ) {
        MainScreenNavigation(navController)
    }
}

@Composable
fun MainScreenNavigation(navController: NavHostController) {
    NavHost(navController, startDestination = Screen.Profile.route!!) {
        composable(Screen.Profile.route) {}
        composable(Screen.Explore.route!!) {}
        composable(Screen.Favorites.route!!) {}
        composable(Screen.PickUp.route!!) {}
        composable(Screen.Camera.route!!) {}
    }
}

@Composable
fun BottomNav(navController: NavController) {
    val navBackStackEntry by navController.currentBackStackEntryAsState()
    val currentRoute = navBackStackEntry?.destination
    BottomAppBar(cutoutShape = CircleShape, modifier = Modifier.height(64.dp)) {
        Row {
            items.forEachIndexed { index, it ->
                if (index != 2) {
                    // Main item
                    BottomNavigationItem(
                        icon = {
                            it.icon?.let {
                                Icon(
                                    imageVector = it,
                                    contentDescription = "",
                                    modifier = Modifier.size(35.dp),
                                    tint = Color.White

                                )
                            }
                        },
                        label = {
                            it.title?.let {
                                Text(
                                    text = it,
                                    color = Color.White
                                )
                            }
                        },
                        selected = currentRoute?.hierarchy?.any { it.route == it.route } == true,
                        selectedContentColor = Color(R.color.purple_700),
                        unselectedContentColor = Color.White.copy(alpha = 0.4f),
                        onClick = {}
                    )
                } else {
                    // placeholder for center fab
                    BottomNavigationItem(
                        icon = {},
                        label = { },
                        selected = false,
                        onClick = { },
                        enabled = false
                    )
                }
            }
        }

    }
}