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
相关产品推荐
相关产品推荐

