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

如何在不使用linked_scroll_controller时同步多个独立SingleChildScrollView滚动

实现多水平滚动行同步(不使用linked_scroll_controller或外层SingleChildScrollView)

当前代码仅用两个ScrollController管理4行滚动,导致滚动时无法实现所有行同步。要解决这个问题,我们可以通过监听每个控制器的滚动事件,手动同步其他控制器的滚动位置,同时避免循环触发监听逻辑。

解决方案核心思路

  • 将组件改为StatefulWidget,用于维护滚动监听和状态标记
  • 为每一行创建独立的ScrollController
  • 添加布尔变量_isScrolling,防止代码触发的滚动再次触发监听器,造成循环
  • 为每个控制器绑定滚动监听器,滚动时同步所有其他控制器的偏移量
  • 在组件销毁时清理所有监听器和控制器,避免内存泄漏

完整实现代码

import 'package:flutter/material.dart';

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

  @override
  State<SynchroniseScroll> createState() => _SynchroniseScrollState();
}

class _SynchroniseScrollState extends State<SynchroniseScroll> {
  late List<ScrollController> _scrollControllers;
  bool _isScrolling = false;

  @override
  void initState() {
    super.initState();
    // 为4行滚动创建独立控制器
    _scrollControllers = List.generate(4, (index) => ScrollController());
    // 为每个控制器绑定滚动监听
    for (var controller in _scrollControllers) {
      controller.addListener(_syncScroll);
    }
  }

  void _syncScroll() {
    // 若为代码触发的滚动,直接返回避免循环
    if (_isScrolling) return;
    setState(() => _isScrolling = true);

    // 获取当前触发滚动的控制器偏移量
    final currentOffset = _scrollControllers.firstWhere(
      (controller) => controller.hasClients && controller.offset != controller.position.minScrollExtent,
      orElse: () => _scrollControllers[0],
    ).offset;

    // 同步所有其他控制器的滚动位置
    for (var controller in _scrollControllers) {
      if (controller.hasClients && controller.offset != currentOffset) {
        controller.jumpTo(currentOffset);
      }
    }

    // 延迟重置标记,允许下次用户触发的滚动
    Future.delayed(const Duration(milliseconds: 50), () {
      setState(() => _isScrolling = false);
    });
  }

  @override
  void dispose() {
    // 清理所有监听器并销毁控制器
    for (var controller in _scrollControllers) {
      controller.removeListener(_syncScroll);
      controller.dispose();
    }
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('同步滚动示例'),
      ),
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          ...List.generate(
            4,
            (index) => Padding(
              padding: const EdgeInsets.only(top: 18.0),
              child: SingleChildScrollView(
                controller: _scrollControllers[index],
                scrollDirection: Axis.horizontal,
                child: Row(
                  children: [
                    ...List.generate(
                      25,
                      (itemIndex) => Container(
                        height: 100,
                        width: 100,
                        color: itemIndex.isEven ? Colors.yellowAccent : Colors.purpleAccent,
                      ),
                    )
                  ],
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

关键细节说明

  • _isScrolling标记:区分用户手动滚动和代码触发的滚动,避免监听器循环调用
  • 独立控制器:确保每一行的滚动状态都能被精准控制和同步
  • 资源清理:在dispose方法中移除监听器并销毁控制器,防止内存泄漏

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 23:42:28