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

Flutter中如何让两个SingleChildScrollView同步垂直滚动?

Flutter实现两个SingleChildScrollView同步垂直滚动

要实现两个SingleChildScrollView的同步垂直滚动,核心是通过ScrollController监听滚动事件,同步两个视图的滚动偏移,以下是具体实现方案:

基础同步(内容高度一致)

如果两个滚动视图的内容总高度相同,可直接同步滚动偏移:

class SyncScrollDemo extends StatefulWidget {
  const SyncScrollDemo({super.key});

  @override
  State<SyncScrollDemo> createState() => _SyncScrollDemoState();
}

class _SyncScrollDemoState extends State<SyncScrollDemo> {
  late ScrollController _leftController;
  late ScrollController _rightController;
  bool _isSyncing = false; // 避免循环触发滚动监听

  @override
  void initState() {
    super.initState();
    _leftController = ScrollController();
    _rightController = ScrollController();

    // 监听两侧滚动事件,触发同步逻辑
    _leftController.addListener(_syncScroll);
    _rightController.addListener(_syncScroll);
  }

  void _syncScroll() {
    if (_isSyncing) return;
    _isSyncing = true;

    final currentController = WidgetsBinding.instance.focusedScrollController;
    if (currentController == _leftController) {
      _rightController.jumpTo(_leftController.offset);
    } else {
      _leftController.jumpTo(_rightController.offset);
    }

    // 延迟重置标记,防止滚动卡顿
    Future.delayed(const Duration(milliseconds: 10), () {
      _isSyncing = false;
    });
  }

  @override
  void dispose() {
    _leftController.dispose();
    _rightController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('同步滚动示例')),
      body: Row(
        children: [
          // 左侧滚动视图
          Expanded(
            child: SingleChildScrollView(
              controller: _leftController,
              child: Column(
                children: List.generate(50, (i) => ListTile(title: Text('左侧条目 $i'))),
              ),
            ),
          ),
          // 右侧滚动视图
          Expanded(
            child: SingleChildScrollView(
              controller: _rightController,
              child: Column(
                children: List.generate(50, (i) => ListTile(title: Text('右侧条目 $i'))),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

适配内容高度不一致的情况

如果两个视图的内容总高度不同,需按滚动比例计算偏移量,保证滚动进度同步:

修改_syncScroll方法为:

void _syncScroll() {
  if (_isSyncing) return;
  _isSyncing = true;

  final leftMax = _leftController.position.maxScrollExtent;
  final rightMax = _rightController.position.maxScrollExtent;

  if (WidgetsBinding.instance.focusedScrollController == _leftController) {
    // 左侧滚动,按比例计算右侧偏移
    final ratio = rightMax / leftMax;
    _rightController.jumpTo(_leftController.offset * ratio);
  } else {
    // 右侧滚动,按比例计算左侧偏移
    final ratio = leftMax / rightMax;
    _leftController.jumpTo(_rightController.offset * ratio);
  }

  Future.delayed(const Duration(milliseconds: 10), () {
    _isSyncing = false;
  });
}

注意事项

  • _isSyncing标记用于避免循环触发滚动监听,防止卡顿或无限循环
  • 若需要平滑滚动,可将jumpTo替换为animateTo,并设置动画时长和曲线参数
  • 组件销毁时必须调用dispose()释放ScrollController资源

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 08:40:24