Jetpack Compose 如何从视图模型收集流并在可组合项中对它们进行操作?

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

视图模型:

    class RulesViewModel : ViewModel() {
        private val _sharedFlow = MutableSharedFlow<ScreenEvents>()
        val sharedFlow = _sharedFlow.asSharedFlow()

        sealed class ScreenEvents {
            data class ShowSnackbar(val message: String) : ScreenEvents()
            data class Navigate(val route: String) : ScreenEvents()
        }
    }

可组合:

@Composable
fun EventListener(
    rulesVm: RulesViewModel,
) {
    LaunchedEffect(key1 = true) {
        rulesVm.sharedFlow.collect { event ->
            when(event) {
                is RulesViewModel.ScreenEvents.ShowSnackbar -> {
                    SnackbarScreen("snackbar ${event.message}")
                }
                is RulesViewModel.ScreenEvents.Navigate -> {
                    // todo
                }
            }
        }
    }
}

这给出了一条错误消息:@Composable 调用只能在 @Composable 函数的上下文中发生

从 viewModel 收集流并在可组合项中对它们进行操作的最佳实践是什么?

android kotlin android-fragments mvvm android-jetpack-compose
2个回答
2
投票

最好使用扩展 - Flow。collectAsState()

您的情况是:

val screenState = rulesVm.sharedFlow.collectAsState

然后在可组合函数的主体中,您可以:

@Composable
fun EventListener(
rulesVm: RulesViewModel,
) {
    val screenState = rulesVm.sharedFlow.collectAsState()
    
    when(screenState) {
             is RulesViewModel.ScreenEvents.ShowSnackbar -> {
                 SnackbarScreen("snackbar ${event.message}")
             }
             is RulesViewModel.ScreenEvents.Navigate -> {
                 // todo
             }
    }
}

您无法在 LaunchedEffect 主体和 Flow.collect() 内部调用可组合函数,因为它是协程作用域的扩展,而不是可组合函数。


0
投票

我认为您希望看到以下行为:您调度一个事件,并且小吃栏出现在当前屏幕上,而无需转到另一个屏幕。如果您在接收事件时不需要考虑应用程序生命周期,那么下面的示例将适合您。我创建了一个scaffoldState,用它来显示Snaskbar。由于 Unit 是一个对象,因此订阅发生一次。

val scaffoldState = rememberScaffoldState()
LaunchedEffect(Unit) {
    rulesVm.sharedFlow.collect { event ->
        when(event) {
            is RulesViewModel.ScreenEvents.Navigate -> TODO("Add navigation to another screen")
            is RulesViewModel.ScreenEvents.ShowSnackbar -> 
                scaffoldState.snackbarHostState.showSnackbar(
                    message = "snackbar ${event.message}"
                )
        }
    }
}

Scaffold(
    scaffoldState = scaffoldState,
    snackbarHost = { snackbarHostState ->
        SnackbarHost(snackbarHostState) { data ->
            Snackbar(
                snackbarData = data
            )
        }
    }
) {
    TODO("Add your screen content")
}
© www.soinside.com 2019 - 2024. All rights reserved.