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

能否在Jetpack Compose中实现三态模态底部表单?

自定义Compose三状态模态底部表单实现方案

直接基于Compose的swipeable modifier实现三状态(Peek/半展开/全展开)底部表单,无需嵌套XML,同时支持底层地图交互:

核心思路

通过定义三个滑动锚点对应不同状态,结合swipeable处理手势切换,仅在表单可见区域响应滑动,确保底层地图可正常交互。

1. 定义状态枚举

enum class BottomSheetState {
    Closed,
    Peek,
    HalfExpanded,
    Expanded
}

2. 计算各状态偏移量

根据屏幕尺寸定义不同状态的位置:

val screenHeight = LocalConfiguration.current.screenHeightDp.dp
val statusBarHeight = WindowInsets.statusBars.getTop(LocalDensity.current).dp
// 自定义各状态高度
val peekHeight = 60.dp
val halfExpandedHeight = screenHeight / 2
val expandedHeight = screenHeight - statusBarHeight

// 锚点映射:y偏移量 -> 对应状态
val anchors = mapOf(
    screenHeight to BottomSheetState.Closed, // 完全隐藏在屏幕下方
    0.dp to BottomSheetState.Peek, // 仅显示peek高度
    -(halfExpandedHeight - peekHeight) to BottomSheetState.HalfExpanded, // 半展开
    -(expandedHeight - peekHeight) to BottomSheetState.Expanded // 全展开(顶部贴合状态栏)
)

3. 构建可滑动表单

使用rememberSwipeableState管理状态,结合offset实现位置动画:

val sheetState = rememberSwipeableState(initialValue = BottomSheetState.HalfExpanded)
val isSheetVisible = sheetState.currentValue != BottomSheetState.Closed

AnimatedVisibility(visible = isSheetVisible) {
    Box(
        modifier = Modifier
            .fillMaxWidth()
            .offset(y = sheetState.offset.value)
    ) {
        Box(
            modifier = Modifier
                .fillMaxWidth()
                .height(expandedHeight)
                .background(MaterialTheme.colorScheme.surface)
                .clip(RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp))
                .swipeable(
                    state = sheetState,
                    anchors = anchors,
                    orientation = Orientation.Vertical,
                    // 禁用阻力,让滑动切换更顺滑
                    resistance = null
                )
        ) {
            Column(modifier = Modifier.fillMaxSize()) {
                // 顶部拖动指示器
                Box(
                    modifier = Modifier
                        .align(Alignment.CenterHorizontally)
                        .padding(16.dp)
                        .size(40.dp, 4.dp)
                        .background(
                            MaterialTheme.colorScheme.onSurface.copy(alpha = 0.3f),
                            RoundedCornerShape(2.dp)
                        )
                )

                // 表单内容区域(示例用LazyColumn)
                val scrollState = rememberLazyListState()
                val nestedScrollConnection = remember {
                    object : NestedScrollConnection {
                        override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
                            // 解决列表滚动与表单滑动的冲突:列表顶置时,滑动触发表单状态切换
                            val delta = available.y
                            return if (delta > 0 && scrollState.firstVisibleItemIndex == 0 && scrollState.firstVisibleItemScrollOffset == 0) {
                                Offset(0f, sheetState.performDrag(delta))
                            } else {
                                Offset.Zero
                            }
                        }
                    }
                }

                LazyColumn(
                    modifier = Modifier
                        .weight(1f)
                        .nestedScroll(nestedScrollConnection),
                    state = scrollState
                ) {
                    items(20) { index ->
                        Text(
                            "表单条目 $index",
                            modifier = Modifier
                                .fillMaxWidth()
                                .padding(16.dp)
                        )
                    }
                }
            }
        }
    }
}

关键细节说明

  • 底层地图交互:仅表单可见区域(Peek状态下的60dp高度、半展开/全展开的全部区域)会响应滑动手势,其他区域的点击/触摸会直接透传到底层地图组件。
  • 状态切换逻辑:
    • 初始为半展开状态,上滑触发全展开,下滑触发Peek状态
    • Peek状态下继续下滑,表单完全关闭
    • 全展开状态下滑,返回半展开状态
  • 自定义调整:可通过修改peekHeight、halfExpandedHeight参数,灵活调整各状态的显示高度。

内容的提问来源于stack exchange,提问作者Scoobidy-Bop

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 03:35:55