如何移除Compose中BottomNavigationItem的左右默认内边距?
问题背景
导航项标签文本较长时,官方BottomNavigationItem自带的左右内边距会压缩内容展示空间,只能通过文本缩放适配,无法让导航项占满全部可用宽度。
原有实现代码如下:
@Composable fun BottomNavBar( backStackEntryState: State<NavBackStackEntry?>, navController: NavController, bottomNavItems: List<NavigationItem> ) { BottomNavigation( backgroundColor = DarkGray.copy(alpha = 0.6f), elevation = Dimen0, modifier = Modifier .padding(Dimen10, Dimen20, Dimen10, Dimen20) .clip(RoundedCornerShape(Dimen13, Dimen13, Dimen13, Dimen13)) ) { bottomNavItems.forEach { item -> val isSelected = item.route == backStackEntryState.value?.destination?.route BottomNavigationItem( icon = { Icon( painter = painterResource(id = item.icon.orZero()), contentDescription = stringResource(id = item.title) ) }, label = { ResponsiveText( text = stringResource(id = item.title), textStyle = TextStyle14, maxLines = 1 ) }, selectedContentColor = Color.White, unselectedContentColor = Color.White, alwaysShowLabel = true, selected = isSelected, onClick = { navController.navigate(item.route) { navController.graph.startDestinationRoute?.let { route -> popUpTo(route = route) { saveState = true } } launchSingleTop = true restoreState = true } }, modifier = if (isSelected) { Modifier .clip(RoundedCornerShape(Dimen13, Dimen13, Dimen13, Dimen13)) .background(color = DarkGray) } else { Modifier.background(color = Color.Unspecified) } ) } } }
原因说明
官方Material组件库的BottomNavigationItem内部硬编码了12dp的水平内边距,没有提供公开的修改参数,直接给组件设置padding或offset无法彻底消除内置间距。
解决方案
方案1:自定义导航项(全版本通用,推荐)
放弃使用官方封装的BottomNavigationItem,直接在BottomNavigation作用域内用基础组件自定义导航项,完全控制布局、边距和交互逻辑,示例代码:
@Composable fun BottomNavBar( backStackEntryState: State<NavBackStackEntry?>, navController: NavController, bottomNavItems: List<NavigationItem> ) { BottomNavigation( backgroundColor = DarkGray.copy(alpha = 0.6f), elevation = Dimen0, modifier = Modifier .padding(Dimen10, Dimen20, Dimen10, Dimen20) .clip(RoundedCornerShape(Dimen13)) ) { bottomNavItems.forEach { item -> val isSelected = item.route == backStackEntryState.value?.destination?.route Box( modifier = Modifier .weight(1f) .clip(RoundedCornerShape(Dimen13)) .background(if (isSelected) DarkGray else Color.Unspecified) .clickable( interactionSource = remember { MutableInteractionSource() }, indication = null, onClick = { navController.navigate(item.route) { navController.graph.startDestinationRoute?.let { route -> popUpTo(route) { saveState = true } } launchSingleTop = true restoreState = true } } ) .padding(vertical = 8.dp), contentAlignment = Alignment.Center ) { Column( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(4.dp) ) { Icon( painter = painterResource(id = item.icon.orZero()), contentDescription = stringResource(id = item.title), tint = Color.White ) Text( text = stringResource(id = item.title), style = TextStyle14, color = Color.White, maxLines = 1, overflow = TextOverflow.Ellipsis ) } } } } }
- 通过
weight(1f)让每个导航项平均分配导航栏宽度,完全占满可用空间 - 水平边距、图标文字间距、点击效果、选中态样式全部可自定义,没有组件内置限制
- 适配所有Material2、Material3版本,不存在版本兼容问题
方案2:覆盖CompositionLocal参数(仅Material3 1.1.0+可用)
如果使用的是高版本Material3组件库,可通过覆盖局部参数修改默认水平边距,无需完全重写导航项:
CompositionLocalProvider( LocalNavigationItemHorizontalPadding provides 0.dp ) { BottomNavigation( // 原有BottomNavigation参数不变 ) { bottomNavItems.forEach { item -> val isSelected = item.route == backStackEntryState.value?.destination?.route BottomNavigationItem( // 原有BottomNavigationItem参数不变 modifier = Modifier .weight(1f) .then( if (isSelected) { Modifier .clip(RoundedCornerShape(Dimen13)) .background(color = DarkGray) } else { Modifier.background(color = Color.Unspecified) } ) ) } } }
注意:该方案仅支持Material3 1.1.0及以上版本,Material2组件未暴露该配置参数,无法使用。
内容的提问来源于stack exchange,提问作者R0ck
相关产品推荐
相关产品推荐

