You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现LazyColumn与Pager内可见LazyColumn的滚动同步?

实现左右LazyColumn滚动同步的优化方案

问题根源

你尝试共享单个rememberLazyListState无效的核心原因是:Compose中每个LazyColumn必须持有独立的LazyListState实例,多个列表共用一个状态会导致状态管理混乱,尤其是Pager中的非活跃页面LazyColumn会被重组或处于休眠状态,无法正确响应状态变化。

优化实现方案

我们需要为每个需要同步的LazyColumn创建独立状态,通过监听滚动状态和Pager页面切换事件,双向同步滚动位置,避免循环触发:

  • 为左侧StaticColumn和Pager中每个需要同步的页面LazyColumn创建独立的LazyListState
  • 监听Pager页面切换,将新激活页面的LazyColumn滚动到与左侧一致的位置
  • 监听左侧和当前活跃右侧LazyColumn的滚动状态,双向同步滚动位置(添加判断避免循环触发)

修改后的完整代码

@Stable
data class EntryState(
    val id: Int = 0,
)

@Preview(showBackground = true)
@Composable
fun Entry(
    modifier: Modifier = Modifier,
) {
    val entryState = remember {
        mutableStateListOf(EntryState())
    }
    repeat(100) { entryState.add(EntryState(id = entryState.last().id + 1)) }

    // 左侧StaticColumn的滚动状态
    val leftScrollState = rememberLazyListState()
    // 存储Pager中每个页面的滚动状态(仅页面1需要同步,这里为扩展性保留所有页面)
    val pagerScrollStates = remember {
        listOf(
            rememberLazyListState(), // 页面0
            rememberLazyListState(), // 页面1(需要同步)
            rememberLazyListState()  // 页面2
        )
    }

    Row(
        modifier
            .fillMaxSize()
            .background(Color(0xFFededf4))
    ) {
        StaticColumn(
            Modifier.weight(0.35f),
            entries = entryState,
            scrollState = leftScrollState
        )
        SwipeableColumnSet(
            Modifier.weight(0.65f),
            entries = entryState,
            leftScrollState = leftScrollState,
            pagerScrollStates = pagerScrollStates
        )
    }
}

@Composable
fun StaticColumn(
    modifier: Modifier = Modifier,
    entries: SnapshotStateList<EntryState>,
    scrollState: LazyListState
) {
    LazyColumn(
        modifier = modifier,
        state = scrollState
    ) {
        item { 
            Box(modifier.padding(start = 8.dp)) { 
                Text(text = "Length", fontSize = 20.sp) 
            } 
        }
        items(
            items = entries,
            key = { entry -> entry.id }
        ) { entry ->
            Box(
                modifier
                    .fillMaxWidth()
                    .shadow(
                        elevation = 4.dp,
                        spotColor = Color.White,
                        shape = RoundedCornerShape(4.dp)
                    )
                    .padding(start = 8.dp, top = 1.dp)
            ) { Text(text = "${entry.id}") }
        }
    }
}

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun SwipeableColumnSet(
    modifier: Modifier = Modifier,
    entries: SnapshotStateList<EntryState>,
    leftScrollState: LazyListState,
    pagerScrollStates: List<LazyListState>
) {
    val pagerState = rememberPagerState(1) { 3 }
    val currentRightScrollState = pagerScrollStates[pagerState.currentPage]

    // 当Pager切换页面时,同步新页面的滚动位置到左侧状态
    LaunchedEffect(pagerState.currentPage) {
        currentRightScrollState.scrollToItem(
            index = leftScrollState.firstVisibleItemIndex,
            scrollOffset = leftScrollState.firstVisibleItemScrollOffset
        )
    }

    // 同步左侧滚动到右侧当前页面
    LaunchedEffect(leftScrollState.firstVisibleItemIndex, leftScrollState.firstVisibleItemScrollOffset) {
        // 避免循环触发:仅当右侧状态与左侧不一致时才同步
        if (currentRightScrollState.firstVisibleItemIndex != leftScrollState.firstVisibleItemIndex ||
            currentRightScrollState.firstVisibleItemScrollOffset != leftScrollState.firstVisibleItemScrollOffset
        ) {
            currentRightScrollState.scrollToItem(
                index = leftScrollState.firstVisibleItemIndex,
                scrollOffset = leftScrollState.firstVisibleItemScrollOffset
            )
        }
    }

    // 同步右侧当前页面滚动到左侧
    LaunchedEffect(currentRightScrollState.firstVisibleItemIndex, currentRightScrollState.firstVisibleItemScrollOffset) {
        // 避免循环触发:仅当左侧状态与右侧不一致时才同步
        if (leftScrollState.firstVisibleItemIndex != currentRightScrollState.firstVisibleItemIndex ||
            leftScrollState.firstVisibleItemScrollOffset != currentRightScrollState.firstVisibleItemScrollOffset
        ) {
            leftScrollState.scrollToItem(
                index = currentRightScrollState.firstVisibleItemIndex,
                scrollOffset = currentRightScrollState.firstVisibleItemScrollOffset
            )
        }
    }

    HorizontalPager(
        state = pagerState,
        modifier = modifier
            .fillMaxSize()
            .background(Color.Yellow)
    ) { page ->
        Column(
            modifier = Modifier
                .fillMaxWidth()
                .clickable { }
        ) {
            when (page) {
                0 -> Column() { }
                1 -> Column(modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
                    Text(text = "Side Berms", fontSize = 20.sp, modifier= Modifier.padding(start = 8.dp))
                    Row {
                        Text("Left", modifier = Modifier
                            .padding(start = 8.dp, top = 1.dp)
                            .weight(1f))
                        Text("Right", modifier = Modifier
                            .padding(start = 8.dp, top = 1.dp)
                            .weight(1f))
                    }
                    LazyColumn(
                        modifier = modifier,
                        state = pagerScrollStates[page]
                    ) {
                        items(
                            items = entries,
                            key = { entry -> entry.id }
                        ) { entry ->
                            Box(
                                modifier
                                    .fillMaxWidth()
                                    .shadow(
                                        elevation = 4.dp,
                                        spotColor = Color.White,
                                        shape = RoundedCornerShape(4.dp)
                                    )
                                    .padding(start = 8.dp, top = 1.dp)
                            ) {
                                Row {
                                    Text(text = "${entry.id}",
                                        Modifier
                                            .padding(start = 8.dp, top = 1.dp)
                                            .weight(1f))
                                    Text(text = "${entry.id}",
                                        Modifier
                                            .padding(start = 8.dp, top = 1.dp)
                                            .weight(1f))
                                }
                            }
                        }
                    }
                }
                2 -> Column() { }
            }
        }
    }
}

方案优势

  • 每个LazyColumn持有独立状态,符合Compose状态管理规范
  • 通过LaunchedEffect监听状态变化,仅在必要时触发同步,避免无效重组
  • 处理了Pager页面切换的同步场景,保证切换后滚动位置一致
  • 添加了状态一致性判断,避免循环触发滚动事件

内容的提问来源于stack exchange,提问作者MSP

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 04:05:18