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

Jetpack Compose中SwipeToDismissBox搭配Snackbar撤销的实现问题

解决Jetpack Compose滑动删除列表项后恢复时重复删除的问题

问题说明

刚接触Jetpack Compose,要实现滑动删除列表项+Snackbar恢复的功能,数据存储在Room数据库中。目前滑动删除和恢复的逻辑技术上可正常执行,但恢复后的项会立刻被再次删除——原因是rememberSwipeToDismissBoxState()会记住之前的滑动状态,恢复后复用Composable节点时,旧状态触发了删除判断逻辑。

解决方案

核心是正确管理滑动状态的生命周期,避免旧状态残留:

  • 用官方回调替代直接状态判断:不要在Composable根节点判断滑动状态来触发删除,改用SwipeToDismissBox的onDismissed回调——该回调仅在滑动动画完成后触发一次,避免重复执行删除逻辑。
  • 删除后立即重置滑动状态:在删除完成后调用swipeToDismissState.reset(),将状态恢复至默认值,确保恢复后的项不会携带已滑动的状态标记。
  • 保持列表项key的唯一性:你已使用historyId作为LazyColumn列表项的key,这能确保删除/恢复时Composable节点正确复用,无需修改。

修改后的代码

列表项组件(WatchHistoryItem)

@Composable
fun WatchHistoryItem(
    item: WatchHistoryVideoEntity,
    onDelete: (id: Int) -> Unit,
) {
    val swipeToDismissState = rememberSwipeToDismissBoxState()

    SwipeToDismissBox(
        state = swipeToDismissState,
        backgroundContent = {},
        // 滑动完成后触发的回调,仅执行一次
        onDismissed = { dismissValue ->
            if (dismissValue == SwipeToDismissBoxValue.EndToStart) {
                onDelete(item.historyId)
                // 重置滑动状态,避免恢复后残留已滑动标记
                swipeToDismissState.reset()
            }
        }
    ) {
        VideoItem(item = item.video)
    }
}

列表页面(WatchHistory)

原有逻辑无需大幅修改,确保ViewModel的deleteEntry和undoDelete方法正确更新Room数据即可——Room会自动通知LazyPagingItems重组,恢复的项会重新出现在列表中,且此时滑动状态已重置,不会触发重复删除。

额外说明

原代码中直接在Composable根节点判断swipeToDismissState.currentValue的写法存在问题:会导致每次重组都检查状态,甚至在滑动过程中就触发删除,完全不符合预期。重置状态的操作必须在删除后立即执行,否则当项恢复时,复用的Composable节点会保留旧的滑动状态,再次触发删除逻辑。

内容的提问来源于stack exchange,提问作者Sunner

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 03:44:52