我已经实现了在 android compose 中管理单次事件的方法,例如显示小吃栏,根据 Marco Cattaneo 在medium.com 上的这篇文章。
它使用视图模型中的消息通道。在此通道中插入消息应在撰写和显示小吃栏中触发一次性事件。在可组合函数中使用
LaunchedEffects
和 channel.collect
来显示小吃栏。
但是我有问题,如果在触发小吃栏后发生重组,则不会显示小吃栏。当我在 uiState 中进行更改并同时向通道发送新消息时,Snackbar 不会显示,因为重组会立即破坏它。如果我更改 uiState 并以一定的延迟(500 毫秒)向通道发送新消息,则小吃栏会很好地显示,因为所有重组都已完成。
有什么办法可以防止小吃店因重组而被破坏吗? Toast 消息效果很好。我只对小吃店有问题。
查看模型:
...
private val _uiState: MutableStateFlow<UiStateClass> = MutableStateFlow(UiStateClass())
val uiState: StateFlow<UiStateClass> get() = _uiState.asStateFlow()
private val _snackMessage = Channel<String>(capacity = Channel.BUFFERED)
val snackMessage: Flow<String>
get() = _snackMessage.receiveAsFlow()
...
查看:
@Composable
fun MyScreen(
viewModel: MyViewModel,
) {
val uiState by viewModel.uiState.collectAsState()
val snackbarHostState = remember { SnackbarHostState() }
Scaffold(
snackbarHost = { SnackbarHost(hostState = snackbarHostState) }
) { innerPadding ->
// Main part of user interface
ScreenContent(
uiState
...
)
val lifecycleOwner = LocalLifecycleOwner.current
val context = LocalContext.current
// Displaying snackbar
LaunchedEffect(viewModel.snackMessage) {
lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.snackMessage.collect { msg ->
snackbarHostState.showSnackbar(msg)
}
}
}
}
}
}
如果我仅将某些内容插入到视图模型中的
_snackMessage
中,则小吃栏会正确显示。如果我更改 uiState 并同时将某些内容插入到 _snackMessage
中,则不会显示小吃栏。如果我更改 uiState 并使用 _snackMessage
将某些内容插入到 delay(500)
中,则会显示小吃栏。
您遇到的 Snackbar 由于重组而消失的问题可能是因为
LaunchedEffect
正在与可组合项的其余部分一起重组,导致 Snackbar 过早消失。为了防止这种情况,您可以使用 DisposableEffect
来代替,它不受重组的影响。
以下是如何修改代码以使用
DisposableEffect
:
@Composable
fun MyScreen(
viewModel: MyViewModel,
) {
val uiState by viewModel.uiState.collectAsState()
val snackbarHostState = remember { SnackbarHostState() }
Scaffold(
snackbarHost = { SnackbarHost(hostState = snackbarHostState) }
) { innerPadding ->
// Main part of user interface
ScreenContent(
uiState
...
)
val lifecycleOwner = LocalLifecycleOwner.current
val context = LocalContext.current
DisposableEffect(Unit) {
val job = lifecycleOwner.lifecycleScope.launch {
viewModel.snackMessage.collect { msg ->
snackbarHostState.showSnackbar(msg)
}
}
onDispose {
job.cancel()
}
}
}
}
通过使用
DisposableEffect
,您可以确保 Snackbar 消息收集协程仅在可组合项第一次组合时启动一次,并且当可组合项不再使用时它将被正确处置。这应该可以防止 Snackbar 由于重组而被过早关闭。