Implementing Paging in Jetpack Compose
Tags: android, kotlin, jetpackcompose
Published: 2020-11-04 16:44:47

Jetpack Compose Alpha06 was recently released and along with it, came the new [paging-compose](https://developer.android.com/jetpack/androidx/releases/paging#paging_compose_version_100_2) library. The library provides integration between the [Paging 3.0](https://developer.android.com/topic/libraries/architecture/paging/v3-overview) library and Jetpack Compose.

Both Paging and Jetpack Compose are in alpha rightnow so expect the API to have changes in near future. 

This article will be a short one focused more on integrating paging with compose rather than specifics of paging 3.0 itself.

The code snippets used in this article are from 
 {% github kriticalflare/Rick-Morty-Compose no-readme %} 

## Adding the dependencies 

Add the following dependencies to an existing Jetpack Compose project or create a new project using the compose template from the latest Android Studio Canary. 

```
app/build.gradle


     def paging_compose_version = "1.0.0-alpha01"
     implementation "androidx.paging:paging-compose:$paging_compose_version"

```


## Setting up the Data Source
One can setup the paging data source for jetpack compose in the same fashion as one does for the legacy view based world. 

```
class CharactersPaging {

    fun getCharacters(): PagingSource<Int, Character> {
        return object : PagingSource<Int, Character>() {
            override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Character> {
                val pageNumber = params.key ?: 0

                val charactersResponse = RickMortyClient.INSTANCE.getAllCharacters(page = pageNumber)
                val characters = charactersResponse.results

                val prevKey = if (pageNumber > 0) pageNumber - 1 else null
                val nextKey = if (charactersResponse.info.next != null) pageNumber + 1 else null

                return LoadResult.Page(
                    data = characters,
                    prevKey = prevKey,
                    nextKey = nextKey
                )
            }
        }
    }
}

```

I have used the [Rick And Morty](https://rickandmortyapi.com/documentation) API for this example. The API gives us an `Info` object in response which provides information about the previous and next pages (if they exist).

## Connecting the data source to the UI

The library comes with special extension functions on the [LazyList Scope](https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/LazyListScope#summary) used by the lazy column and row composables which makes declarative lists very easy to create. 


We create a Pager which provides us a flow of PagingData backed by the data source we built earlier.

```
val charactersPaging = remember {
                CharactersPaging()
            }
val pager = remember {
                Pager(
                    PagingConfig(
                        pageSize = 20,
                        enablePlaceholders = true,
                    )
                ) {                  
                 charactersPaging.getCharacters()
                }
            }
```
This flow can then be converted to the [lazyPagingItems](https://developer.android.com/reference/kotlin/androidx/paging/compose/LazyPagingItems) using the extensions from paging compose as follows.

```
val lazyPagingItems: LazyPagingItems<Character> = pager.flow.collectAsLazyPagingItems()
```

These lazyPagingItems can be then passed to the `items` and `itemsIndexed` [extension methods](https://developer.android.com/reference/kotlin/androidx/paging/compose/package-summary.html#extension-functions-summary) on the LazyList Scope. Android Studio has trouble importing the correct extension methods in this case, so make sure to add these imports manually if you face any issues.

```
import androidx.paging.compose.items
import androidx.paging.compose.itemsIndexed
```

Now in typical declarative ui fashion, we can emit different composables depending on different loadStates.

```
LazyColumn{
                if (lazyPagingItems.loadState.refresh == LoadState.Loading) {
                    item {
                        Column(
                            modifier = Modifier.fillParentMaxSize(),
                            verticalArrangement = Arrangement.Center,
                            horizontalAlignment = Alignment.CenterHorizontally
                        ) {
                            CircularProgressIndicator()
                        }
                    }
                }

                itemsIndexed(lazyPagingItems) { index, item ->
                    if (item != null) {
                        CharacterItem(
                            modifier = Modifier.padding(8.dp),
                            character = item,
                            onClick = onCharSelect
                        )
                    }
                }

                if (lazyPagingItems.loadState.append == LoadState.Loading) {
                    item {
                        CircularProgressIndicator(
                            modifier = Modifier.fillMaxWidth()
                                .wrapContentWidth(Alignment.CenterHorizontally)
                        )
                    }
                }
          }
```

## The final implementation

```
@ExperimentalLazyDsl
@Composable
fun CharacterScreen(
    modifier: Modifier = Modifier.fillMaxSize(),
    onCharSelect: (Int) -> Unit
) {
    Surface {
        Box(
            modifier = modifier,
        ) {
            val charactersPaging = remember {
                CharactersPaging()
            }
            val pager = remember {
                Pager(
                    PagingConfig(
                        pageSize = 20,
                        enablePlaceholders = true,
                    )
                ) {                  charactersPaging.getCharacters()
                }
            }

            val lazyPagingItems: LazyPagingItems<Character> = pager.flow.collectAsLazyPagingItems()

            LazyColumn{
                if (lazyPagingItems.loadState.refresh == LoadState.Loading) {
                    item {
                        Column(
                            modifier = Modifier.fillParentMaxSize(),
                            verticalArrangement = Arrangement.Center,
                            horizontalAlignment = Alignment.CenterHorizontally
                        ) {
                            CircularProgressIndicator()
                        }
                    }
                }

                itemsIndexed(lazyPagingItems) { index, item ->
                    if (item != null) {
                        CharacterItem(
                            modifier = Modifier.padding(8.dp),
                            character = item,
                            onClick = onCharSelect
                        )
                    }
                }

                if (lazyPagingItems.loadState.append == LoadState.Loading) {
                    item {
                        CircularProgressIndicator(
                            modifier = Modifier.fillMaxWidth()
                                .wrapContentWidth(Alignment.CenterHorizontally)
                        )
                    }
                }
            }
        }
    }
}
```

Its pretty clear that Jetpack Compose makes implementation of Paging 3.0 much simpler than the legacy view system. You can check out the project used in this article for more examples integrating Paging, Navigation Component, Edge to Edge UI and much more  

 {% github kriticalflare/Rick-Morty-Compose %} 

Thank you for reading! You can get in touch with me here on [Dev](dev.to/kriticalfare) , [Github](github.com/kriticalflare) or [Linkedin](https://www.linkedin.com/in/krithik-suvarna/)