Reputation: 1378
I develop the app in Compose and MVVM architecture. I have viewModel with state of view per screen. 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
}
}
}
State:
class ProfileViewState {
val nick: MutableStateFlow<String> = MutableStateFlow("default nick")
}
When user press the button I change value of "nick" in state.
_state.value.nick.value = "new nick"
I want to observe the state in my view and update data automatically when they changed
@Composable
fun ProfileScreen() {
val viewModel: ProfileViewModel = viewModel()
val viewState by viewModel.state.collectAsState()
}
TextView where I try to display "nick" value
Text(text = viewState.nick.value, style = MaterialTheme.typography.h4)
The problem is that when I change data in state, screen doesn't react and doesn't update. When I open ProfileScreen I get the same instance of state(?) with default values
Upvotes: 2
Views: 4272
Reputation: 364858
With simple case you can use mutableStateOf("default nick")
in the ProfileViewState
instead of MutableStateFlow
.
Something like:
class ProfileViewState {
var nick by mutableStateOf("default nick")
}
and in your composable:
Text(text = viewState.value.nick, style = MaterialTheme.typography.h4)
Button(onClick = { viewState.value.nick= "new nick"}){Text("Update")}
If you want to use the MutableStateFlow
you have to observe the nick
.
Something like:
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")}
In this case you can also provide a function in the viewModel to update the nickname:
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
}
}
and then in your composable just use:
Button(
onClick = { viewModel.onNickChange("new nick2")}
){ /*..*/ }
Upvotes: 4
Reputation: 7690
This is probably because you are using nested StateFlow
s and collecting only the outer one, while updating the inner one. I suggest moving to the following implementation:
data class ProfileViewState(val nick: String, ...)
// Update it like:
fun updateNick(nick: String){
_state.value = _state.value.copy(nick = nick)
}
This way viewModel.state.collectAsState()
should work
Upvotes: 1