setupWithNavController() function handles the multiple backstack and that is really great:
bottomNavigationView.setupWithNavController(getNavController())
But I had some issues with that. To be on the same page, let's review the scenario one more time quickly:
just assume there are three tabs, each representing a separate nested graph.
As I mentioned in my question, using setupWithNavController sometimes causes to call onViewCreated function by navigating between tabs.
I discovered that even in some circumstances, the fragment would recreate each time. (when you click on another tab while the current tab is showing a fragment that is not the start the destination for the selected nested graph), the fragment is going to recreate each time.
Problems which I had:
- collecting viewModel's flows in the fragment (when onViewCreated is called but the fragment is not recreated):
To solve this issue, I decided to use viewLifecycleOwner.lifecycleScope instead of the lifecycleScope of the fragment.
However, by navigating to another tab, the fragment still existed, but the view was destroyed so I could make sure that switching between tabs would not create the issue of having multiple observations of flows.
viewModel.notifyList.flowWithLifecycle(
viewLifecycleOwner.lifecycle,
Lifecycle.State.RESUMED
).onEach {
doSomething()
}.launchIn(viewLifecycleOwner.lifecycleScope)
- storing the last state of lists (when the fragment is recreated)
So if the fragment is going to be recreated each time, how could I store the state of my recyclerview adapter, which is implemented with the pagination3 library?
So as you know, there is a paging source that returns a flow of PagingData. Let's say there is a function named getData()
private fun getData(): Flow<PagingData<LeitnerWordModel>> {
return Pager(
config = PagingConfig(),
initialKey = 0,
pagingSourceFactory = {
/**Pager expected to create a new instance. Ensure that the pagingSourceFactory passed to Pager always returns a new instance of PagingSource.*/
MyPagingSource()
}
).flow
}
Previously I called getData() from the fragment, but by recreating the fragment, I used to lose the last state of the list as every time it had to create a new instance of the Pager.
So as all we know, the ViewModel has a longer lifecycle, so I just called getData() in the init block of viewModel only once.
And used a Stateflow (or SharedFlow with replay=1) to emit the last recent data to the fragment:
class MyViewModel:ViewModel(){
private val _notifyList =
MutableSharedFlow<PagingData<MyModel>>(replay = 1, extraBufferCapacity = 0, onBufferOverflow = BufferOverflow.SUSPEND)
val notifyList: SharedFlow<PagingData<MyModel>> get() = _notifyList
init {
getData().cachedIn(viewModelScope).onEach {
_notifyList.emit(it)
}.launchIn(viewModelScope)
}
}