You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何禁用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 12:48:09