【问题标题】:Global Snackbar Handling Jetpack Compose全球小吃店处理 Jetpack Compose
【发布时间】:2021-11-17 13:14:33
【问题描述】:

鉴于我有多个可以触发小吃店的地方,我希望在我的应用中有一个中心位置,我可以在其中处理显示/关闭小吃店。

这是我的应用程序的结构:

我已经实现了一个包含 StateFlow 的 BaseViewModel,它应该跟踪 SnackBar 消息(所有其他 ViewModel 都继承自这个 BaseViewModel):

@HiltViewModel
open class BaseViewModel @Inject constructor() : ViewModel() {
    val _snackBarMessage = MutableStateFlow("")
    val snackBarMessage: StateFlow<String> = _snackBarMessage
}

为了测试 StateFlow 的更新是否正确触发,我实现了一条消息,应该在每次登录后更新 StateFlow:

private fun setSnackBarMessage() {
   _snackBarMessage.value = "A wild snackBar appeared"
}

MainContent 包含我的 Scaffold(包括scaffoldState、snackbarHost),应该对snackBarMessage 流中的变化做出反应,并在需要时显示/关闭 Snackbar:

fun MainContent(...){
   val message by viewModel.snackBarMessage.collectAsState()

   LaunchedEffect(message) {
      if (message.isNotEmpty() Timber.d("We got a snackbar")
   }

   Scaffold(...){...}
}

在调试过程中,我注意到每次登录后,snackBarMessage 值都会正确更新,但 MainContent 没有得到这些更新,这反过来意味着,snackbar 永远不会显示。

MainContent 没有从 LoginComposable 获取这些更新是否有原因? 是否有可能有一个小吃店的中心实例,或者我真的需要在每个 Composable 中单独处理小吃店?

【问题讨论】:

    标签: android kotlin android-jetpack-compose android-snackbar


    【解决方案1】:

    您的问题的原因可能是使用消息作为LaunchedEffect 的键,而不是同时更改消息。在文档中,您可以阅读该类型的副作用将在密钥修改后重新启动。

    如果 LaunchedEffect 使用不同的键重新组合(请参阅 下面的重启效果部分),现有的协程将 取消,新的挂起功能将在新的 协程。

    Compose 中的一些效果,例如 LaunchedEffect、produceState 或 DisposableEffect,采用可变数量的参数,键,它们是 用来取消运行效果,用新的开始一个新的 键。

    我建议用包含snackbar 内容的字段将snackbar 消息包装在某种对象(不是数据类)中。

    干杯

    【讨论】:

    • 但是消息永远不会改变(总是“”)。我也尝试过使用LaunchedEffect(Unit),但仍然没有变化。
    • Unit 是一个对象,也是一个常量。您是否尝试过更改消息?
    • 是的,我有。我的问题是在 MainContent 中没有观察到消息的任何更改,当消息在另一个可组合(例如登录)中更改时
    • 在这种情况下,您使用的是几个 BaseVM 实例而不是一个。确保 MainContent 使用的 viewModel 是 setSnackBarMessage 交互的唯一实例。您不能在其他 viemModels 上调用此方法并期望小吃栏,因为每个视图模型都有另一个 stateflow 实例
    【解决方案2】:
    You can use this
    
    @Composable
    fun MainScreen() {
        val coroutineScope = rememberCoroutineScope()
        val showSnackBar: (
            message: String?,
            actionLabel: String,
            actionPerformed: () -> Unit,
            dismissed: () -> Unit
        ) -> Unit = { message, actionLabel, actionPerformed, dismissed ->
            coroutineScope.launch {
                val snackBarResult = scaffoldState.snackbarHostState.showSnackbar(
                    message = message.toString(),
                    actionLabel = actionLabel
                )
                when (snackBarResult) {
                    SnackbarResult.ActionPerformed -> actionPerformed.invoke()
                    SnackbarResult.Dismissed -> dismissed.invoke()
                }
            }
        }
    
    
        //Global using
        showSnackBar.invoke(
            "YOUR_MESSAGE",
            "ACTION_LABEL",
            {
             //TODO ON ACTION PERFORMED
            },
            {
             //TODO ON DISMISSED
            }
        )
    }
    

    【讨论】:

      猜你喜欢
      • 2021-04-16
      • 2022-11-06
      • 2022-11-10
      • 2022-10-22
      • 2021-12-12
      • 2020-07-25
      • 1970-01-01
      • 2021-10-27
      • 2022-06-29
      相关资源
      最近更新 更多