I do have a problem initializing a mutablestate fo...
# compose
t
I do have a problem initializing a mutablestate for compose. The following code will show in the composable the text "Init" instead of "Loaded"
Copy code
class TestViewModel(ctx: Application): AndroidViewModel(ctx) {
    var value by mutableStateOf("Start")

    init {
        value = "Init"
        viewModelScope.launch(Dispatchers.IO) {
            value = "Loaded"
        }
    }
}
@Composable
fun Test(vm: TestViewModel = viewModel()) {
    Text(vm.value)
}
When i change the init to:
Copy code
init {
    value = "Initialized"
    viewModelScope.launch(Dispatchers.IO) {
        withContext(Dispatchers.Main) {
            value = "Loaded"
        }
    }
}
It shows "Loaded" Also a delay would make the code work. Normally i think changing a compose state variable it is not necessary to be on the UI Thread
z
This is probably because the coroutine runs before the snapshot that creates the
value
state object has been applied so as far as that coroutine is concerned the state doesn't exist yet. There are two things that would likely work here. Probably the better option, since it's good practice for other reasons, is to perform state mutations on a background thread in an explicit snapshot:
Copy code
value = "Init"
viewModelScope.launch(<http://Dispatchers.IO|Dispatchers.IO>) {
  Snapshot.withMutableSnapshot {
    value = "Loaded"
  }
}
This is nice since it also handles updating multiple states from that coroutine and ensures the updates are all published together at the same time. Another option is to explicitly tell the snapshot system after your states are initialized:
Copy code
value = "Init"
Snapshot.notifyObjectsInitialized()
viewModelScope.launch(<http://Dispatchers.IO|Dispatchers.IO>) {
  value = "Loaded"
}
This doesn't handle updating multiple states from a background thread though.
t
Hi Zach, thank you very much to take time for this answer. Unfortunately both solutions are not working in my minimal example. I like to use mutable states in ViewModels because it is much less boilerplate code than using Flows. And i use it a lot. I only see this problems in the init function. It is really annoying.
Could it be a bug?
In the compose runtime
Btw. as soon as i put a delay(10) into the code it works. In my production code i do read a file from disk. But it looks like it is so fast that i get this race condition with the states.
a
When you create a
mutableStateOf
in composition but try to update it in background thread before the composition is completed, the update won't succeed. This is kind of expected due to how the snapshot system works. This used to result in an exception in older compose versions: https://kotlinlang.slack.com/archives/CJLTWPH7S/p1652084646064769 Now there won't be a crash but as I said the update won't work.
z
Oh wait during composition, then yea the composition has its own snapshot so you can’t see anything created in there until composition applies it.