Eric Chee
06/01/2022, 2:54 PMMutableStateFlow
for updating and a public StateFlow
that uses .stateIn()
Example:
class ExampleViewModel: ViewModel() {
private val _state = MutableStateFlow<Int>(0) // Set initial value
val state: StateFlow<Int> = _state
.stateIn(
scope = ...,
started = ...,
initialValue = 0 // Have to duplicate initial value :(
)
fun onSomeAction() {
_state.getAndUpdate { lastState -> lastState + 1 }
}
}
Joffrey
06/01/2022, 2:55 PMstateIn
here?Joffrey
06/01/2022, 2:56 PMEric Chee
06/01/2022, 3:04 PMcombine(<ExternalFlows>) {
...
}.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = AuthorScreenUiState(AuthorUiState.Loading, ...)
)
Joffrey
06/01/2022, 3:11 PMEric Chee
06/01/2022, 4:16 PM.stateIn()
for consuming cold flows into a StateFlow in the case i want those cold flows to stay active depending on the subscriber sharing behavior i defined?
• B) If the ViewModel is the creator of a MutableStateFlow, i dont need to use .stateIn() since that mutable stateFlow is already hot and will stay alive for the lifecycle of the ViewModel, regardless if theres no collectorsEric Chee
06/01/2022, 4:42 PMclass ExampleViewModel(externalIntFlow: Flow<Int>): ViewModel() {
data class State(val msg: String, val isLoading: Boolean = false)
private val _state = MutableSharedFlow<State>()
val state = externalIntFlow.map { action ->
when (action) {
1 -> State("One")
else -> State("?")
}
}.merge(_state)
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
initialValue = State("Empty")
)
fun someAction() = viewModelScope.launch {
/*
Want to update last value of `state` but dont get benefit of state.getAndUpdate
since state is not mutable from stateIn(), but instead have to use external sharedFlow + state.value.copy()
*/
_state.emit(state.value.copy(isLoading = false))
delay(1000)
_state.emit(State("Success"))
}
}
Joffrey
06/01/2022, 4:48 PMstateIn
needs a scope - to start this coroutine behind the scenes. And it's also why you define a policy to keep this coroutine alive or cancel it. If you create a mutable state flow directly, you just created some sort of observable state variable. It doesn't need any coroutine on its own, because the state will be updated by other pieces of code (they may use coroutines themselves, but they don't need to because state flows have a state
property that can be updated without suspending).
B) trueJoffrey
06/01/2022, 4:54 PMstateIn
on the external cold flows, you could just manually launch a coroutine that collects them and updates your state flow. This way you can also update your state flow using _state.value=x
in someAction()
(it will not be read-only)Eric Chee
06/01/2022, 5:12 PMclass ExampleViewModel(externalIntFlow: Flow<Int>): ViewModel() {
data class State(val msg: String, val isLoading: Boolean = false)
private val _state = MutableStateFlow(State("Empty"))
val state = _state.asStateFlow()
init {
viewModelScope.launch {
externalIntFlow.map { action ->
when (action) {
1 -> State("One")
else -> State("?")
}
}.also { _state.emitAll(it) }
}
}
fun someAction() = viewModelScope.launch {
_state.getAndUpdate { it.copy(isLoading = false) }
delay(1000)
_state.update { State("Success") }
}
}
Joffrey
06/01/2022, 5:14 PMephemient
06/01/2022, 5:54 PMprivate val _state = MutableStateFlow<Int>(0)
val state: StateFlow<Int>
get() = _state.asStateFlow()
Joffrey
06/01/2022, 5:56 PM