Joe
Joe

Reputation: 471

How to sync LazyColumn scroll in HorizontalPager - Jetpack Compose

I have a LazyColumn inside a HorizontalPager which lets me have different pages where each one is scrollable. This is for a calendar app that has a page for each day.

HorizontalPager(state = pagerState) { index->
    LazyColumn {
    
        <...content here...>
                    
    }
}

But I'm not sure how to sync the scroll state between pages. Currently if I scroll to halfway down the first page and swipe to the next, it starts at the top again.

How would I have all pages keep the same scroll?

Upvotes: 3

Views: 498

Answers (1)

BenjyTec
BenjyTec

Reputation: 10777

Unfortunately, it seems not to be very easy to get this functionality to work. Each LazyColumn must have its own LazyListState, thus you can't just assign a single one to all LazyColumns.

So you would need some logic to manually synchronize them and store a separate global scroll state.
I wrote the following code, which however has the drawback that under certain situations, when you swipe the HorizontalPager while the LazyColumn is still scrolling, the scroll state sometimes is not correctly applied.

You can test it and see whether it is acceptable in your case. Unfortunately, I wasn't able to track down the actual issue. It seems that scrollToItem sometimes is blocking until you swipe to that page, and then the scroll is not correctly applied.

However, here is my code:

val pagerState = rememberPagerState(pageCount = { 3 })

var firstVisibleItemIndex by remember{
    mutableIntStateOf(0)
}
var firstVisibleItemScrollOffset by remember {
    mutableIntStateOf(0)
}

HorizontalPager(state = pagerState) { thisPage ->

    val localScrollState = rememberLazyListState()
    var wasScrolled by remember { mutableStateOf(false) }

    LaunchedEffect(firstVisibleItemIndex, firstVisibleItemScrollOffset) {
        // apply global scroll state
        wasScrolled = false
        localScrollState.scrollToItem(firstVisibleItemIndex, firstVisibleItemScrollOffset)
    }

    LaunchedEffect(localScrollState.isScrollInProgress) {
        if (localScrollState.isScrollInProgress) {
            wasScrolled = true
        } else if (wasScrolled) {
            // write to global scroll state
            firstVisibleItemIndex = localScrollState.firstVisibleItemIndex
            firstVisibleItemScrollOffset = localScrollState.firstVisibleItemScrollOffset
        }
    }

    LazyColumn(
        state = localScrollState
    ) {
        items(50) {
            Text(modifier = Modifier
                .padding(16.dp)
                .fillMaxWidth(), text = "ITEM $it");
        }
    }
}

Upvotes: 4

Related Questions