小吃栏随着重组而消失

问题描述 投票:0回答:1

我已经实现了在 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)
中,则会显示小吃栏。

android kotlin android-jetpack-compose android-snackbar
1个回答
0
投票

您遇到的 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 由于重组而被过早关闭。

© www.soinside.com 2019 - 2024. All rights reserved.