'Paging 3 list auto refresh on navigation back in jetpack compose navigation

I am using Jetpack Compose, along with Paging 3 library & Jetpack Navigation. The issue I am facing is I have a LazyList which is fetching data from remote source using paging library.

ViewModel

fun getImages(): Flow<PagingData<ObjectImage>> = Pager(
        PagingConfig(PAGE_SIZE, enablePlaceholders = false)
    ) { DataHome(RANDOM) }.flow.cachedIn(viewModelScope)

HomeView

val images = viewModelHome.getImages().collectAsLazyPagingItems()
LazyColumn {
  ...
}

Now whats happening is when I navigate to another View using navHostController.navigate() and then press back to get to HomeView... the LazyColumn resets itself & start loading items again from network.

So I am stuck with this issue. I tried manually caching in viewModel variable... though it works but it screws up SwipeRefresh (which stops showing refresh state)

data.apply {
            when {
                // refresh
                loadState.refresh is LoadState.Loading -> {
                    ItemLoading()
                }

                // reload
                loadState.append is LoadState.Loading -> {...}

                // refresh error
                loadState.refresh is LoadState.Error -> {...}

                // reload error
                loadState.append is LoadState.Error -> {...}
            }
        }
implementation("androidx.paging:paging-runtime-ktx:3.1.0")
implementation("androidx.paging:paging-compose:1.0.0-alpha14")

Is this an issue with PagingLibrary which is still in alpha??

Update 1 (I am not sure if this is a good solution, but I am solving the swipe refresh issue as follows)

// get images
    var images: Flow<PagingData<ObjectImage>> = Pager(PagingConfig(PAGE_SIZE)) {
        DataHome(RANDOM)
    }.flow.cachedIn(viewModelScope)

    // reload items
    fun reload(){
        images = Pager(PagingConfig(PAGE_SIZE)) {
            DataHome(RANDOM)
        }.flow.cachedIn(viewModelScope)
    }

// and rather than calling .refresh() method on lazy items... I am calling viewModel.reload()


Solution 1:[1]

The problem is that you are creating new Pager every time you call getImages(), which is every time your composable recomposes, that's not how it's supposed to be done.

You should make it a val items = Pager(... for the caching to work.

For the screwed up SwipeRefresh, how do you implement it? There is a refresh() method on LazyPagingItems, you should use that.


EDIT: Ok, so based on the coments and edits to your question:

In your viewmodel, do as I suggested before:

val items = Pager( // define your pager here

Your composable can then look like this:

@Composable
fun Screen() {
    val items = viewModel.items.collectAsLazyPagingItems()
    val state = rememberSwipeRefreshState(
        isRefreshing = items.loadState.refresh is LoadState.Loading,
    )

    SwipeRefresh(
        modifier = Modifier.fillMaxSize(),
        state = state,
        // use the provided LazyPagingItems.refresh() method,
        // no need for custom solutions
        onRefresh = { items.refresh() }
    ) {
        LazyColumn(
            modifier = Modifier.fillMaxSize(),
        ) {
            // display the items only when loadState.refresh is not loading,
            // as you wish
            if (items.loadState.refresh is LoadState.NotLoading) {
                items(items) {
                    if (it != null) {
                        Text(
                            modifier = Modifier.padding(16.dp),
                            text = it,
                        )
                    }
                }
                // you can also add item for LoadState.Error, anything you want
                if (items.loadState.append is LoadState.Loading) {
                    item {
                        Box(modifier = Modifier.fillMaxWidth()) {
                            CircularProgressIndicator(
                                modifier = Modifier
                                    .align(Alignment.Center)
                                    .padding(16.dp)
                            )
                        }
                    }
                }
            }
            // if the loadState.refresh is Loading,
            // display just single loading item,
            // or nothing at all (SwipeRefresh already indicates
            // refresh is in progress)
            else if (items.loadState.refresh is LoadState.Loading) {
                item {
                    Box(modifier = Modifier.fillParentMaxSize()) {
                        Text(
                            text = "Refreshing",
                            modifier = Modifier.align(Alignment.Center))
                    }
                }
            }
        }
    }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1