Paginated Lazy List

Syed Taha Alam
3 min readFeb 6, 2023

Why care?

Many of you would likely agree that lists are a ubiquitous component in mobile apps. Despite their often long length, users only view a segment of them. Hence, loading every item all at once is not an ideal approach. Pagination solves this issue by dividing long lists into manageable pages that can be loaded one at a time. In simpler terms, pagination helps break up long lists into smaller, more easily loaded pages.

Composable

If you are not familiar yet with the basics of Jetpack Compose.

In compose the Column is equivalent to the listView.

@Composable
fun SimpleList() {
Column {
Text("column element 1")
Text("column element 2")
for (i in 0..10) {
Text("Unlimited items")
}
}
}

as ListView You can have many elements inside the column.

but there is another function that acts similar to the RecyclerView

  LazyColumn(
state = lazyListState,

modifier = Modifier
.fillMaxWidth()
) {
itemsIndexed(items = data) { _, item ->
item(item)
}
item{
if (refereshing) {

loader()
}
}
}

in which we can detect the end of the list by its state.

val lazyListState = rememberLazyListState()

create a sscroll context that save the list state

data class ScrollContext(
val isTop: Boolean,
val isBottom: Boolean,
)

create some extension over the lazyliststate


val LazyListState.isLastItemVisible: Boolean
get() {
val visibleItemsInfo = layoutInfo.visibleItemsInfo
return if (layoutInfo.totalItemsCount == 0) {
false
} else {
val lastVisibleItem = visibleItemsInfo.last()
val viewportHeight = layoutInfo.viewportEndOffset + layoutInfo.viewportStartOffset

(lastVisibleItem.index + 1 == layoutInfo.totalItemsCount &&
lastVisibleItem.offset + lastVisibleItem.size <= viewportHeight)
}
}
val LazyListState.lastItem: Int
get() = layoutInfo.totalItemsCount - 1

val LazyListState.isFirstItemVisible: Boolean
get() = firstVisibleItemIndex == 0

@Composable
fun rememberScrollContext(listState: LazyListState): ScrollContext {
val scrollContext by remember {
derivedStateOf {
ScrollContext(
isTop = listState.isFirstItemVisible,
isBottom = listState.isLastItemVisible
)
}
}
return scrollContext
}

to check if needed to make the paginated call you can use this

 // update the data when it has reached to the bottom of the page
if (!stopReloading.value && scrollContext.isBottom) {

//prevent duplicate event due to recompose too quickly
if (!refereshing && (lazyListState.lastItem ) >= (pageNumber.value * pageSize)) {

if ( checkNeededToLoadedNewData(pageNumber.value, totalPages)) {
if (pageNumber.value >= 1) {
pageNumber.value++
paginationCall()
}
}else{
stopReloading.value = true
}
}
}

by combining these component you could achieve a composable that can create a list and show loading composable while loading new data.


@Composable
fun <T> PaginatedColumn(
modifier: Modifier = Modifier,
item: @Composable (data: T) -> Unit,
loader: @Composable () -> Unit ,
paginationCall: () -> Unit,
pageSize: Int = 10,
totalPages: Long = 10,
data: List<T>,
refereshing:Boolean = false
)
{
var pageNumber = remember {
mutableStateOf(1)
}
var stopReloading = remember{
mutableStateOf(false)
}


val lazyListState = rememberLazyListState()

Column(modifier = modifier) {
LazyColumn(
state = lazyListState,

modifier = Modifier
.fillMaxWidth()
) {
itemsIndexed(items = data) { _, item ->
item(item)
}
item{
if (refereshing) {

loader()
}
}
}
}


// manipulate the scroll context according to the list state
val scrollContext = rememberScrollContext(lazyListState)

// update the data when it has reached to the bottom of the page
if (!stopReloading.value && scrollContext.isBottom) {

//prevent duplicate event due to recompose too quickly
if (!refereshing && (lazyListState.lastItem ) >= (pageNumber.value * pageSize)) {

if ( checkNeededToLoadedNewData(pageNumber.value, totalPages)) {
if (pageNumber.value >= 1) {
pageNumber.value++
paginationCall()
}
}else{
stopReloading.value = true
}
}
}
}

you can checkout the complete code

PaginatedColumn/paginatedColumn at master · SyedTahaAlam/PaginatedColumn (github.com)

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

Syed Taha Alam
Syed Taha Alam

Written by Syed Taha Alam

Software Engineer passionate about the web and all the latest tech 🌐

No responses yet