如何禁用BottomSheetScaffold完全展开时的弹跳/跳动效果
问题
在项目中使用BottomSheetScaffold时,底部面板完全展开后向上滑动会出现弹跳/跳动,露出背景内容。需要移除这个效果,让面板完全展开时向上滑动不再弹跳。

相关代码(忽略自定义主题值)
val scaffoldState = rememberBottomSheetScaffoldState() var currentSheetStat by remember { mutableStateOf(SheetContent.BookingRide) } var sheetPeekHeight by remember { mutableStateOf(420.dp) } BottomSheetScaffold( scaffoldState = scaffoldState, modifier = Modifier.fillMaxSize(), sheetPeekHeight = sheetPeekHeight, sheetShadowElevation = 0.dp, sheetTonalElevation = 0.dp, containerColor = Color.Transparent, sheetContent = { Column { Column( Modifier .padding( start = BnTheme.gaps.component, end = BnTheme.gaps.component, bottom = BnTheme.gaps.component ) .fillMaxWidth()) { Row(verticalAlignment = Alignment.CenterVertically) { if(currentSheetStat == SheetContent.RideStarted) { BnPebbleMedium(pebbleText = stringResource(id = commonR.string.sos), hasLeadingIcon = true, leadingIcon = painterResource(id = commonR.drawable.ic_alert_badge), leadingIconTint = BnTheme.colors.error, pebbleTextColor = BnTheme.colors.error, pebbleContainer = BnTheme.colors.surfaceContainer) } Spacer(modifier = Modifier.weight(1f)) Card( shape = RoundedCornerShape(100), colors = CardDefaults.cardColors( containerColor = BnTheme.colors.surfaceContainer ), modifier = Modifier .size(48.dp) ) { Column(horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center, modifier = Modifier.fillMaxSize()) { Icon( painter = painterResource(id = commonR.drawable.ic_locate), contentDescription = null, Modifier.size(24.dp), tint = shades_dark_blue_0 ) } } } } Card( Modifier .fillMaxWidth() .background(Color.Transparent), shape = RoundedCornerShape(topStart = BnTheme.cornerRadius.crL, topEnd = BnTheme.cornerRadius.crL), colors = CardDefaults.cardColors( containerColor = BnTheme.colors.surfaceContainer ), elevation = CardDefaults.cardElevation(defaultElevation = 2.dp) ) { AnimatedContent( targetState = currentSheetStat, label = "", ) { Crossfade(targetState = it, label = "bottom sheet animation") { when(it) { SheetContent.BookingRide -> { //not important } SheetContent.ConfirmPickupLocation -> { //not important } SheetContent.SendingRequest -> { sheetPeekHeight = 220.dp SheetContentSendingRequest{ currentSheetStat = SheetContent.NavigatorArrival } } SheetContent.NavigatorArrival -> { // not important } SheetContent.RideStarted -> { //not important } } } } } } }, sheetContainerColor = Color.Transparent, sheetDragHandle = { HorizontalDivider(thickness = 0.dp, color = Color.Transparent) } ) { Box(modifier = Modifier.fillMaxSize()) { ScreenContent() // the background map implementation } }
面板内容代码
@Composable fun SheetContentSendingRequest( onRideRequestSent : () -> Unit ) { Column(modifier = Modifier .fillMaxWidth() .padding( horizontal = BnTheme.gaps.component, vertical = BnTheme.gaps.element )) { SheetDragHandle() Spacer(modifier = Modifier.height(BnTheme.gaps.component)) Column ( Modifier .padding(vertical = BnTheme.gaps.element) .fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally){ BnText( text = stringResource(id = commonR.string.sent_ride_request), style = BnTheme.typography.titleSmallSemiBold, color = BnTheme.colors.bodyPrimary ) Row { Row { BnText( text = stringResource(commonR.string.you_will_reach_destination_by), style = BnTheme.typography.bodyMediumMedium, color = BnTheme.colors.bodySecondary ) Spacer(modifier = Modifier.height(BnTheme.gaps.mini)) BnText( text = "11:11 AM", style = BnTheme.typography.bodyMediumMedium, color = BnTheme.colors.link ) } } } Spacer(modifier = Modifier.height(BnTheme.gaps.component)) Row { LinearProgressIndicator( progress = 1f, trackColor = BnTheme.colors.primaryButtonOff, color = BnTheme.colors.primaryButtonOn, modifier = Modifier .weight(1f) .padding(horizontal = 12.dp) .clip(RoundedCornerShape(100)) ) LinearProgressIndicator( progress = 0f, trackColor = BnTheme.colors.primaryButtonOff, color = BnTheme.colors.primaryButtonOn, modifier = Modifier .weight(1f) .padding(horizontal = 12.dp) .clip(RoundedCornerShape(100)) ) LinearProgressIndicator( progress = 0f, trackColor = BnTheme.colors.primaryButtonOff, color = BnTheme.colors.primaryButtonOn, modifier = Modifier .weight(1f) .padding(horizontal = 12.dp) .clip(RoundedCornerShape(100)) ) LinearProgressIndicator( progress = 0f, trackColor = BnTheme.colors.primaryButtonOff, color = BnTheme.colors.primaryButtonOn, modifier = Modifier .weight(1f) .padding(horizontal = 12.dp) .clip(RoundedCornerShape(100)) ) } Spacer(modifier = Modifier.height(BnTheme.gaps.component)) Icon(painter = painterResource(id = commonR.drawable.vector_shared_ride), contentDescription = null, tint = Color.Unspecified, modifier = Modifier .height(124.dp) .align(Alignment.CenterHorizontally) .noRippleClickable { onRideRequestSent() }) Spacer(modifier = Modifier.height(BnTheme.gaps.component)) }
解决方案
方法1:嵌套滚动拦截器(推荐)
通过自定义NestedScrollConnection拦截展开状态下的向上滑动事件,阻止面板弹跳:
val scaffoldState = rememberBottomSheetScaffoldState() var currentSheetStat by remember { mutableStateOf(SheetContent.BookingRide) } var sheetPeekHeight by remember { mutableStateOf(420.dp) } // 自定义滚动拦截逻辑 val nestedScrollConnection = remember { object : NestedScrollConnection { override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { val isSheetExpanded = scaffoldState.bottomSheetState.currentValue == BottomSheetValue.Expanded // 面板展开且向上滑动时,拦截所有滑动事件 return if (isSheetExpanded && available.y < 0) { available } else { Offset.Zero } } } } BottomSheetScaffold( scaffoldState = scaffoldState, // 添加嵌套滚动拦截 modifier = Modifier.fillMaxSize().nestedScroll(nestedScrollConnection), sheetPeekHeight = sheetPeekHeight, sheetShadowElevation = 0.dp, sheetTonalElevation = 0.dp, containerColor = Color.Transparent, sheetContent = { // 原面板内容 }, sheetContainerColor = Color.Transparent, sheetDragHandle = { HorizontalDivider(thickness = 0.dp, color = Color.Transparent) } ) { Box(modifier = Modifier.fillMaxSize()) { ScreenContent() } }
原理
onPreScroll会在滚动事件传递给面板前触发,当检测到面板处于完全展开状态且滑动方向向上时,直接拦截事件,避免面板继续向上偏移。向下滑动不受影响,面板仍可正常收起。
方法2:自定义BottomSheetBehavior
通过修改面板的拖动行为,从根源上禁止向上滑动超出展开状态:
// 自定义Behavior,限制最大向上拖动偏移 val customBehavior = rememberBottomSheetBehavior<BottomSheetScaffoldSheetState>() { override fun calculateMaxDragOffset(child: View): Int { return 0 // 禁止向上拖动超出展开状态 } } BottomSheetScaffold( scaffoldState = rememberBottomSheetScaffoldState(bottomSheetState = customBehavior), sheetPeekHeight = sheetPeekHeight, // 其他参数保持不变 ) { // 背景内容 }
这种方式直接修改面板的拖动逻辑,彻底消除弹跳空间。
内容的提问来源于stack exchange,提问作者Atul
相关产品推荐
相关产品推荐

