I've got a problem with a MutableStateFlow property shared between two fragments.
To make it understandable:
I have a BasicViewModel
which should be always one instance for both of the fragments because of the nav graph implementation
private val basicViewModel: basicViewModel by navGraphViewModels(R.id.basic_graph) { defaultViewModelProviderFactory }
This ViewModel have a MutableStateFlow
property declared like this
private val _basicProperty = MutableStateFlow<BasicClass?>(null)
val basicProperty : Flow<BasicClass?> = _basicId
.filterNotNull()
.flatMapConcat { someRepository.getBasicProperty(it) }
.onEach { _basicProperty.value = it }
.catch { }
Then, I have FragmentA
and FragmentB
declared in navigation using nav graphs which calls the property similarly, like this
basicViewModel.basicProperty
.filterNotNull()
.mapNotNull { it.innerProperty}
.onEach { doSomething(it) }
.launchIn(viewLifecycleOwner.lifecycleScope)
It all looks fine, but when I navigate to FragmentA
the flow of BasicProperty loads (data loads from WebApi) then, I navigate to FragmentB
and the flow loads again instead of calling already loaded data which in App it looks kinda lagging because of the reload
Question: What should I do/change to get the already existing data from BasicViewModel
in FragmentB
?
Your _basicProperty
is a hot StateFlow, but you never use it for collecting anything. The property you have exposed, basicProperty
, is a cold flow, so each subscriber that collects it will start a new run of the cold flow. Each of these cold flows will be posting their updates to the MutableStateFlow, so at that point, its state is unpredictable because it's showing the latest thing any collector of the shared cold flow is doing.
I think what you want is for there to be one flow of execution that's shared. So you should have a single StateFlow that is performing the connection, like this:
val basicProperty : StateFlow<BasicClass?> = _basicId
.filterNotNull()
.flatMapConcat { someRepository.getBasicProperty(it) }
.catch { }
.stateIn(viewModelScope, SharingStarted.Eagerly, null)
Your original code doesn't do anything to start the flow until each Fragment comes along to collect it. But this code's call to stateIn
starts the flow a single time in the viewModelScope
(in this case immediately because of the Eagerly
parameter).
Now this flow only runs once. You can still have each Fragment run its own downstream flow like you were already doing.