从 StateFlow 收集状态

Collect state from StateFlow

我在 Compose 和 MVVM 架构中开发应用程序。我有 viewModel,每个屏幕都有视图状态。视图模型:

class ProfileViewModel : ViewModel() {

    private val _state = MutableStateFlow(ProfileViewState())
    val state: StateFlow<ProfileViewState> get() = _state

    val actions = Channel<ProfileAction>()

    init {
        viewModelScope.launch {
            combine(_state) {
                ProfileViewState()
            }.collect {
                _state.value = it
            }
    }
}

州:

class ProfileViewState {
    val nick: MutableStateFlow<String> = MutableStateFlow("default nick")
}

当用户按下按钮时,我会更改状态中“nick”的值。

_state.value.nick.value = "new nick"

我想在我的视图中观察状态并在数据发生变化时自动更新数据

@Composable
fun ProfileScreen() {
        val viewModel: ProfileViewModel = viewModel()
        val viewState by viewModel.state.collectAsState()
}

我尝试在其中显示“昵称”值的 TextView

Text(text = viewState.nick.value, style = MaterialTheme.typography.h4)

问题是当我更改状态中的数据时,屏幕没有反应也没有更新。当我打开 ProfileScreen 时,我得到了具有默认值

的相同状态实例(?)

对于简单的情况,您可以在 ProfileViewState 中使用 mutableStateOf("default nick") 而不是 MutableStateFlow

类似于:

class ProfileViewState {
    var nick  by mutableStateOf("default nick")
}

并在您的可组合项中:

  Text(text = viewState.value.nick, style = MaterialTheme.typography.h4)
  Button(onClick = { viewState.value.nick= "new nick"}){Text("Update")}

如果你想使用 MutableStateFlow 你必须遵守 nick
类似于:

val nick = viewModel.state.value.nick.collectAsState()

Text(text = nick.value, style = MaterialTheme.typography.h4)
Button(onClick = { viewModel.state.value.nick.value  = "new nick"}){Text("Update")}

这种情况下你也可以在viewModel中提供一个函数来更新昵称:

class HelloViewModel : ViewModel() {
    //...
   
    fun onNameChange(newName: String) {
        _name.value = newName
    }
}

class ProfileViewState {

    val nick: MutableStateFlow<String> = MutableStateFlow("default nick")
   
    fun onNickChange(newNick: String) {
        nick.value = newNick
    }
}

然后在您的可组合项中使用:

Button(
     onClick = { viewModel.onNickChange("new nick2")}
){ /*..*/ }

这可能是因为您正在使用嵌套 StateFlow 并且只收集外部的,同时更新内部的。我建议转到以下实现:

data class ProfileViewState(val nick: String, ...)

// Update it like:
fun updateNick(nick: String){
     _state.value = _state.value.copy(nick = nick)
}

这样 viewModel.state.collectAsState() 应该可行