How to collect two state flow in activity? Because mine only the first flow that consumed.
For example, inside viewmodel is like this:
class ExampleViewModel: ViewModel(){
private val state = MutableStateFlow<HomeMainFragmentState>(HomeMainFragmentState.Init)
private val products = MutableStateFlow<List<ProductEntity>>(mutableListOf())
//to be consumed
fun getState() : StateFlow<HomeMainFragmentState> = state
fun getProducts() : StateFlow<List<ProductEntity>> = products
}
And then in my view is like this:
private fun observe(){
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED){
viewModel.getState().collect { state -> handleState(state) }
viewModel.getProducts().collect{ products -> handleProducts(products) }
}
}
}
The problem is, only the first flow is consumed, for this case is the 'state', the products was never consumed/executed by activity/fragment.
How to fix this? I also read about combining the flow, does it mean the second flow depends on first flow to run?
Calling collect
on a Flow suspends the coroutine until the Flow is complete. For a MutableStateFlow, it will only be complete when it's cancelled. So usually, when you call collect
on a Flow, you don't do anything else in the coroutine underneath that call.
If you want to collect each of these flows individually, you need two coroutines.
The flowOnLifecycle
function will give you a bit cleaner code, so it's not as painful to have two coroutines:
private fun observe(){
viewModel.getState()
.flowOnLifecycle(Lifecycle.State.STARTED)
.onEach { state -> handleState(state) }
.launchIn(lifecycleScope)
viewModel.getProducts()
.flowOnLifecycle(Lifecycle.State.STARTED)
.onEach { products -> handleProducts(products) }
.launchIn(lifecycleScope)
}
I also want to mention, function names like getState
are unnatural in Kotlin, except when they are heavy functions (and even if it were a heavy function that has to calculate something, I would prefer generateState
or calculateState
). It's more proper to use a property:
private val mutableState = MutableStateFlow<HomeMainFragmentState>(HomeMainFragmentState.Init)
val state: StateFlow<HomeMainFragmentState> get() = mutableState
They have said that in a future version of Kotlin, there will probably be a better syntax for exposing a read-only version of a mutable class that doesn't require a second property. Something like this:
private val state = MutableStateFlow<HomeMainFragmentState>(HomeMainFragmentState.Init)
public get(): StateFlow<HomeMainFragmentState>