Sahal Nazar
Sahal Nazar

Reputation: 1005

How to create an endless Pager in Jetpack Compose

    Box(
        modifier = Modifier.size(100.dp),
        contentAlignment = Alignment.Center
    ) {
        val pagerState = rememberPagerState()
        val items = listOf("A", "B", "C")
        androidx.compose.foundation.pager.HorizontalPager(
            state = pagerState,
            pageCount = items.size,
            modifier = Modifier,
            verticalAlignment = Alignment.CenterVertically
        ) { page ->
            Text(
                text = items[page],
                modifier = Modifier
            )
        }
    }

In the above code, the pager stops scrolling after reaching the last item. However, I want the pager to continue scrolling endlessly after reaching the last item.

sketch

Upvotes: 7

Views: 4421

Answers (2)

Stefan Medack
Stefan Medack

Reputation: 2808

One small addition to this topic. I noticed that whenever the pageCount changes, the rememberPagerState will not be updated. In this case using the HorizontalPager position with % items.size breaks the calculation and leads to wrong results. Here is a sample that does not have this problem:

@Composable
fun InfiniteHorizontalPager(
    pageCount: Int,
    modifier: Modifier = Modifier,
    initialPage: Int = 0,
    content: @Composable PagerScope.(page: Int) -> Unit,
) {
    val max = Short.MAX_VALUE.toInt()
    val half = max / 2
    
    val pagerPositionIndex = initialPage + half - half % pageCount
    val pagerState = rememberPagerState(pageCount = { max }, initialPage = pagerPositionIndex)

    LaunchedEffect(pagerPositionIndex, pageCount) {
        pagerState.scrollToPage(pagerPositionIndex)
    }

    HorizontalPager(
        state = pagerState,
        userScrollEnabled = pageCount > 1,
        modifier = modifier,
    ) { index ->
        val page = index % pageCount
        this.content(page)
    }
}

Upvotes: 2

Thracian
Thracian

Reputation: 67248

You can create it by setting pageCount to Int.MAX_VALUE and getting modulus of current page to get index for your list of items.

@Preview
@Composable
private fun Test() {

    val pageCount = Int.MAX_VALUE
    val items = listOf("A", "B", "C")
    val pagerState = rememberPagerState(
        initialPage = pageCount / 2
    )

    HorizontalPager(
        modifier = Modifier.fillMaxWidth(),
        pageCount = pageCount,
        state = pagerState
    ) {
         Text(text = items[it % 3])
    }
}

Edit

As of Compose version 1.6.0 Int.MAX_VALUE(2147483647) causes pager to have ANR, to prevent this add a big number but smaller than 2147483647 that user won't bother to scroll that far.

@Preview
@Composable
private fun InfinitePagerSample() {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp)
            .border(2.dp, Color.Red)
    ) {

        val items = remember {
            listOf("A", "B", "C")
        }
        val pageCount = items.size * 400

        val pagerState = rememberPagerState(
            initialPage = pageCount / 2,
            pageCount = {
                pageCount
            }
        )

        HorizontalPager(
            modifier = Modifier.fillMaxWidth(),
            state = pagerState,
            beyondBoundsPageCount = 1
        ) {

            val color = if (it % items.size == 0) {
                Color.Red
            } else if (it % items.size != 1) {
                Color.Yellow
            } else Color.Green

            Column(modifier = Modifier.fillMaxSize().background(color)) {
                SideEffect {
                    println("Page $it, composing...")
                }

                Text(text = items[it % 3], modifier = Modifier.fillMaxSize(), fontSize = 70.sp)

            }
        }
    }
}

Upvotes: 13

Related Questions