4

Using accompanist-pager version 0.25.1, animateScrollToPage() doesn't seem to scroll all the way to make the next page fully visible, the previous page is still shown. The non animated version scrollToPage() seems to work fine however. Am I missing some additional params ?

@ExperimentalPagerApi
@Composable
fun MyPager() {
    val pagerState = rememberPagerState()
    HorizontalPager(
        count = 10,
        state = pagerState
    ) { pagerIndex ->
        Box(
            modifier = Modifier
                .fillMaxWidth()
                .height(150.dp)
                .background(if (pagerIndex.mod(2) == 0) Color.Cyan else Color.Red)
        )
    }
    with(pagerState) {
        LaunchedEffect(key1 = currentPage) {
            launch {
                delay(10000)
                animateScrollToPage(
                    page = (currentPage + 1).mod(pageCount)
                )
            }
        }
    }
}

screenshot

J. Doe
  • 85
  • 12

4 Answers4

9

The problem is that the pagerState updates before the animation ends. To fix this provide another key

var key by remember { mutableStateOf(flase) }

LaunchedEffect(key1 = key) {
    launch {
        delay(2000)
        with(pagerState) {
            val target = if (currentPage < pageCount - 1) currentPage + 1 else 0
            animateScrollToPage(page = target) //Broken
            key = !key
        }
    }
}
obogz_mobile
  • 305
  • 1
  • 11
3

I also had the same problem,In the end I chose to roll back the version of accompanist-pager to 0.24.1-alpha.

李嘉伟
  • 31
  • 1
2

Referencing the above answer, it work fine in 0.29.0-alpha with code below

    with(pagerState) {
        if (pageCount > 0) {
            var key by remember { mutableStateOf(0) }
            LaunchedEffect(key1 = key) {
                launch {
                    delay(5000)
                    val nextPage = (currentPage + 1).mod(pageCount)
                    animateScrollToPage(
                        page = nextPage
                    )
                    key = nextPage
                }
            }
        }
    }
kelvin
  • 33
  • 1
  • 4
1

I took the previous answers and created a composable to handle this:

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun CarouselAutoPlayHandler(
    pagerState: PagerState,
    carouselSize: Int
) {
    var pageKey by remember { mutableStateOf(0) }

    val effectFlow = rememberFlowWithLifecycle(pagerState.interactionSource.interactions)

    LaunchedEffect(effectFlow) {
        effectFlow.collectLatest {
            if (it is DragInteraction.Stop) pageKey++
        }
    }

    LaunchedEffect(pageKey) {
        delay(5000)
        val newPage = (pagerState.currentPage + 1) % carouselSize
        pagerState.animateScrollToPage(newPage)
        pageKey++
    }
}

Explanation:

  1. Pass the pager state and the amount of items
  2. Create a new key and set the initial value as 0 (the value here doesn't matter)
  3. Remember the flow returned from the interactions property in the pager state (You can find the implementation in this article I wrote)
  4. Collect the flow and whenever there's a drag stop, add 1 to the page key so the LaunchedEffect below is restarted
  5. In another LaunchedEffect set the pageKey as the key, add whatever your delay is and after that add the logic to scroll to the next page and change the pageKey to restart the LaunchedEffect once again

The code in the first LaunchedEffect will restart the time whenever the user manually drags the carousel.

You can pass the delay value as a variable, just kept it there hardcoded for the example.

Eury Pérez Beltré
  • 2,017
  • 20
  • 28