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

Android Compose:详情页删记录后返回列表页显示Snackbar示例求助

详情页删除后返回列表页显示带撤销Snackbar的实现方案

下面提供两种可行的实现思路,都是Compose环境下的简洁实现:


方案一:通过导航返回值传递删除信息(推荐)

利用Jetpack Navigation的SavedStateHandle在详情页和列表页间传递删除的记录ID,列表页监听该值并触发Snackbar。

列表页代码实现

@Composable
fun ItemListScreen(navController: NavHostController) {
    val scaffoldState = rememberScaffoldState()
    val scope = rememberCoroutineScope()

    // 监听详情页返回的删除记录ID
    LaunchedEffect(navController) {
        navController.currentBackStackEntry?.savedStateHandle?.getLiveData<String>("deletedItemId")?.observeForever { itemId ->
            itemId?.takeIf { it.isNotEmpty() }?.let {
                scope.launch {
                    val snackbarResult = scaffoldState.snackbarHostState.showSnackbar(
                        message = "已删除记录 $it",
                        actionLabel = "撤销",
                        duration = SnackbarDuration.Long
                    )
                    if (snackbarResult == SnackbarResult.ActionPerformed) {
                        // 执行撤销逻辑:恢复该记录
                        restoreItem(it)
                        // 清除已处理的结果,避免重复触发
                        navController.currentBackStackEntry?.savedStateHandle?.remove<String>("deletedItemId")
                    }
                }
            }
        }
    }

    // 列表UI部分...
}

详情页代码实现

@Composable
fun ItemDetailScreen(navController: NavHostController, itemId: String) {
    val scope = rememberCoroutineScope()

    Button(onClick = {
        // 先执行删除操作
        scope.launch {
            deleteItem(itemId)
            // 传递删除ID到列表页
            navController.previousBackStackEntry?.savedStateHandle?.set("deletedItemId", itemId)
            // 返回列表页
            navController.popBackStack()
        }
    }) {
        Text("删除记录")
    }

    // 详情UI部分...
}

方案二:通过共享ViewModel传递状态

创建一个跨页面共享的ViewModel,详情页触发删除事件,列表页监听事件并显示Snackbar。

共享ViewModel代码

class ItemViewModel : ViewModel() {
    // 用SharedFlow传递删除事件,replay=1确保返回列表页能接收到
    val deletedItemEvent = MutableSharedFlow<String>(replay = 1)

    fun triggerDeleteEvent(itemId: String) {
        viewModelScope.launch {
            deletedItemEvent.emit(itemId)
        }
    }

    // 清除事件缓存,避免重复触发
    fun clearDeleteEvent() {
        deletedItemEvent.resetReplayCache()
    }
}

列表页代码实现

@Composable
fun ItemListScreen(navController: NavHostController) {
    val scaffoldState = rememberScaffoldState()
    val scope = rememberCoroutineScope()
    val viewModel: ItemViewModel = viewModel() // 共享同一个ViewModel实例

    LaunchedEffect(Unit) {
        viewModel.deletedItemEvent.collect { itemId ->
            val snackbarResult = scaffoldState.snackbarHostState.showSnackbar(
                message = "已删除记录 $itemId",
                actionLabel = "撤销",
                duration = SnackbarDuration.Long
            )
            if (snackbarResult == SnackbarResult.ActionPerformed) {
                restoreItem(itemId)
                viewModel.clearDeleteEvent()
            }
        }
    }

    // 列表UI部分...
}

详情页代码实现

@Composable
fun ItemDetailScreen(navController: NavHostController, itemId: String) {
    val scope = rememberCoroutineScope()
    val viewModel: ItemViewModel = viewModel() // 与列表页共享的ViewModel

    Button(onClick = {
        scope.launch {
            deleteItem(itemId)
            viewModel.triggerDeleteEvent(itemId)
            navController.popBackStack()
        }
    }) {
        Text("删除记录")
    }

    // 详情UI部分...
}

注意事项

  • 两种方案中,deleteItem和restoreItem需要根据你的业务逻辑实现(比如操作数据库、更新状态流等)。
  • 导航返回值方案要记得处理完结果后清除SavedStateHandle中的值,避免列表页重建时重复显示Snackbar。
  • 共享ViewModel方案要确保ViewModel是跨页面共享的(默认在同一个导航图内的Composable会共享同一个ViewModel实例)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 11:35:20