Search code examples
androidandroid-jetpack-composejetpack-compose-accompanist

Is there any way to make two horizontal pager of accompanist library to work synchronously?


What I am trying to achieve is if there are two horizontal pagers, then on swiping top one to left then the bottom horizontal pager should swipe to right and vice-versa, have tried using pagerState scrollBy method but not getting desired output


Solution

  • First of all, you need to determine which pager is scrolled and which one should follow it. This can be done with isScrollInProgress, I use it inside derivedStateOf to avoid unnecessary re-compositions.

    Then I run LaunchedEffect. I'm passing pair of pager states in needed order, or null, as a key, so LaunchedEffect will be re-launched when scrolling stops/starts. Using snapshotFlow, it is possible to track changes in the result of a block whose calculations depend on state changes.

    PagerState has scroll position information in the properties currentPage and currentPageOffset. scrollToPage takes only 0..1 values for page offset, but currentPageOffset can be less than zero when scrolling backward.

    Suppose currentPage = 2 and currentPageOffset = -0.1. In this case, I will get 1.9 in pagePart, and I need to split it back to get 1 and 0.9. To do this I use divideAndRemainder: it will return a list of the form listOf(1.0, 0.9).

    Column {
        val count = 10
        val firstPagerState = rememberPagerState()
        val secondPagerState = rememberPagerState()
    
        val scrollingFollowingPair by remember {
            derivedStateOf {
                if (firstPagerState.isScrollInProgress) {
                    firstPagerState to secondPagerState
                } else if (secondPagerState.isScrollInProgress) {
                    secondPagerState to firstPagerState
                } else null
            }
        }
        LaunchedEffect(scrollingFollowingPair) {
            val (scrollingState, followingState) = scrollingFollowingPair ?: return@LaunchedEffect
            snapshotFlow { scrollingState.currentPage + scrollingState.currentPageOffset }
                .collect { pagePart ->
                    val divideAndRemainder = BigDecimal.valueOf(pagePart.toDouble())
                        .divideAndRemainder(BigDecimal.ONE)
    
                    followingState.scrollToPage(
                        divideAndRemainder[0].toInt(),
                        divideAndRemainder[1].toFloat(),
                    )
                }
        }
    
        HorizontalPager(
            count = count,
            state = firstPagerState,
            modifier = Modifier.weight(1f)
        ) {
            Text(it.toString())
        }
        HorizontalPager(
            count = count,
            state = secondPagerState,
            modifier = Modifier.weight(1f)
        ) {
            Text(it.toString())
        }
    }
    

    Result: