Reputation: 113
I have a state flow that emits a value once the HTTP request is returned with the response, the value will be shown as a list in Activity, I'm using Kotlin coroutines StateFlow
for communication between the ViewModel and Activity.
I'm using androidx
lifecycle repeatOnLifecycle
function like this:
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.successFlow.collect { binding.recyclerView.adapter = ExampleAdapter(it) }
}
}
This is working fine at the beginning but then I realized that every time the user goes to another screen and back to the previous screen the state flow will reemit the value which in this case will lose the list state, for example, if the user scrolled to item 10
in the list and then goes to another screen and return back the list will scroll to position 0
because the setAdapter
method invoked again, which is not the case when using LiveData
.
Now I need to handle StateFlow
state and configuration state too, I tried to use the distinctUntilChanged
method but as the documentation says Applying 'distinctUntilChanged' to StateFlow has no effect
.
The question here how I can achieve the same LiveData
behaviour using StateFlow
.
Upvotes: 11
Views: 3264
Reputation: 2408
You have hit the problem that was recently described in a Medium post. Each flow collection starts anew and will receive the value again and again even if it has not changed. LiveData
, on the other hand, knows about which version of the data each observer has seen and will only send data that has been posted again.
A simple fix for your problem would be to store the data somewhere and check if it has changed. You can also collect the flow with flowWithLifecycle()
and do distinctUntilChanged()
on it (beware of using a buffered operator after this or the value emission might be lost, as described in the post linked above).
It would look like this:
viewModel.successFlow.flowWithLifecycle(lifecycle).distinctUntilChanged().onEach {
binding.recyclerView.adapter = ExampleAdapter(it)
}.launchIn(lifecycleScope)
However, I think you should not replace the adapter each time the data changes! You should be doing:
(binding.recyclerView.adapter as ListAdapter<*,*>).submitList(it)
(Of course, you should be using ListAdapter for this to work)
With this nothing will get disrupted when new data arrives, whether is the same or different data.
Upvotes: 7