I need that when the var called CustomSingleton.customVar
stored in a singleton
is modified, my composable gets recomposed to change the current screen in the navhost
. So I did this on the viewmodel
of the screen that contains the navhost
:
val currentScreenId = MutableStateFlow<Int>(CustomSingleton.customVar)
val currentScreenIdState: StateFlow<Int> = currentScreenId.stateIn(
viewModelScope,
SharingStarted.WhileSubscribed(5_000),
CustomSingleton.customVar
)
I listen for the value of the flow on the composable using this code:
val currentSectionId by viewModel.currentScreenIdState.collectAsStateWithLifecycle()
Then, some other part of the code modifies the variable on the singleton:
CustomSingleton.customVar = 1
The issue is that the composable is not being recomposed so the change of the variable is not being observed.
Why?
As Gabe Sechan already pointed out the parameter of MutableStateFlow()
is the initial value, it is not automatically observed for changes.
If you want to observe CustomSingleton.customVar
for changes that itself must be a flow. Assuming customVar
is currently declared like this in CustomSingleton
:
var customVar = 0
Then you can change it to this:
val customVar = MutableStateFlow(0)
0
is the initial value here, replace it with whatever you want. Since this is a Flow now and not an Int, changing the value is done like this:
customVar.value = 42
Now, with this out of the way you can change your view model code to this:
val currentScreenIdState: StateFlow<Int> = CustomSingleton.customVar.asStateFlow()
And that's it. Whenever the value of customVar
is changed your currentScreenIdState
flow emits a new value that is then collected by your UI.
The key is to already use flows at the source for everything that you need to observe for changes. MutableStateFlow
behaves similar to a variable as it always has a single value. But you can use any other type of flow as well (then you maybe need to use stateIn
in the view model again, instead of asStateFlow
). What's best depends on where the changes are actually coming from.
When it is explicitly set by some other parts of your code a MutableStateFlow
is a good choice. When the changed value comes from a callback, then you can convert that into a flow with callbackFlow
. When the data comes from some other API like a database then that may already support flows out-of-the-box. Room, for example, can easily be configured to return all database results as a flow.