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

如何在Material3 Compose中实现Swipeable滑动交互效果

Material3 Compose 实现原Swipeable滑动效果的可行方案

M3 移除旧版Modifier.swipeable和配套的SwipeableState不是砍掉滑动交互能力,而是把原来耦合在Material库中的滑动逻辑拆成了分层的API,适配不同场景的使用需求,不需要找第三方库就能实现和旧版一致甚至更灵活的效果,常用方案按场景选就行:

1. 自定义滑动锚点场景:使用基础API anchoredDraggable

这个是旧版swipeable的直接替代,属于Compose Foundation层提供的底层能力,不绑定Material组件,所有需要自定义滑动锚点、吸附阈值、状态监听的通用滑动场景都可以用,灵活度最高。
核心实现逻辑:

  • 先定义滑动状态枚举,对应不同的锚点位置,比如横向滑动就定义「关闭」「左滑露出操作区」「右滑露出操作区」几个状态
  • 用rememberAnchoredDraggableState初始化滑动状态,配置初始值、位置吸附阈值、速度触发阈值、滑动动画参数
  • 给状态绑定锚点和对应偏移值的映射关系
  • 给目标组件加anchoredDraggable修饰符绑定状态、指定滑动方向,再通过offset修饰符把状态的偏移量应用到组件上即可

最简实现代码参考:

// 定义滑动锚点状态
enum class HorizontalSwipeState { Closed, EndOpened }

@Composable
fun CustomSwipeableItem() {
    val density = LocalDensity.current
    // 左滑后露出的操作区宽度
    val actionAreaWidth = with(density) { 88.dp.toPx() }

    // 初始化滑动状态
    val swipeState = rememberAnchoredDraggableState(
        initialValue = HorizontalSwipeState.Closed,
        // 滑动超过一半距离就吸附到下一个锚点
        positionalThreshold = { fullDistance -> fullDistance * 0.5f },
        // 滑动速度超过阈值直接吸附,不用等位置达标
        velocityThreshold = { with(density) { 120.dp.toPx() } },
        animationSpec = spring(stiffness = Spring.StiffnessMediumLow)
    ).apply {
        // 绑定锚点和对应偏移值
        updateAnchors(
            DraggableAnchors {
                HorizontalSwipeState.Closed at 0f
                HorizontalSwipeState.EndOpened at -actionAreaWidth
            }
        )
    }

    Box(modifier = Modifier.fillMaxWidth().height(64.dp)) {
        // 底层:滑动后露出的操作区域
        Row(
            modifier = Modifier.align(Alignment.CenterEnd).fillMaxHeight(),
            verticalAlignment = Alignment.CenterVertically
        ) {
            IconButton(onClick = { /* 删除逻辑 */ }) {
                Icon(Icons.Default.Delete, contentDescription = "删除")
            }
        }
        // 上层:可滑动的主内容
        Surface(
            modifier = Modifier
                .fillMaxSize()
                .offset { IntOffset(x = swipeState.offset.roundToInt(), y = 0) }
                .anchoredDraggable(
                    state = swipeState,
                    orientation = Orientation.Horizontal
                ),
            shadowElevation = 1.dp
        ) {
            Text("滑动我试试", modifier = Modifier.padding(16.dp))
        }
    }
}

如果需要和列表做嵌套滑动,给Modifier追加nestedScroll(swipeState.nestedScrollConnection)即可,官方已经封装好了嵌套滑动的处理逻辑,比旧版swipeable的嵌套适配简单很多。

2. 列表项滑动删除/滑动操作场景:直接用封装好的SwipeToDismiss组件

如果是做消息列表、待办列表这类常见的滑动删除、滑动标记已读场景,不用自己用底层API搭,M3已经封装好了现成的SwipeToDismiss组件,自带列表复用适配、无障碍支持、滑动冲突处理:

  • 用rememberDismissState初始化组件状态,可自定义滑动方向、不同方向的阈值
  • 通过background参数自定义滑动过程中露出的背景、操作按钮
  • 监听dismissState.currentValue就能拿到当前滑动状态,触发对应业务逻辑

这个组件是M3专门针对列表滑动场景优化的,稳定性比自己写的自定义逻辑好很多。

3. 抽屉、底部弹窗这类系统组件滑动:直接用内置滑动能力

M3提供的ModalNavigationDrawer(侧边模态抽屉)、ModalBottomSheet(模态底部弹窗)、DismissibleDrawerSheet等组件本身已经内置了完整的滑动交互支持,不需要额外加滑动修饰符,直接通过组件参数配置是否允许手势关闭、滑动阈值、动画参数即可。

旧项目迁移提示

如果是从M2版本迁移到M3的存量项目,不想一次性重写所有swipeable逻辑,可以临时用兼容依赖过渡,但长期还是建议切换到上述原生API,旧API后续不会再做性能优化和bug修复,长期维护成本更高。

踩坑提示:不要直接照搬M2版本SwipeableState的偏移计算逻辑,M3的手势管线做了重构,旧的阈值计算、偏移映射逻辑直接套用到新API上会出现跟手性差、吸附位置不准的问题。

内容的提问来源于stack exchange,提问作者אריאל עדן

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 06:27:25