Flutter 同一Row中两个ListView.separated滚动报错及索引联动如何实现?
问题原因
你遇到的报错是因为同一层级的多个垂直滚动ListView默认会复用全局的PrimaryScrollController,同一个控制器不能同时绑定多个滚动位置,所以触发了异常。
解决方案
第一步:修复滚动报错
给两个ListView.separated分别配置独立的ScrollController,同时设置primary: false,禁止使用默认主控制器即可解决报错。
第二步:实现双向索引联动
通过滚动监听实现两个视图的位置同步,注意添加锁标志位避免滚动回调互相触发导致死循环,以下是完整可运行的修改后代码:
class Mainview extends StatefulWidget { const Mainview({Key? key}) : super(key: key); @override _MainviewState createState() => _MainviewState(); } class _MainviewState extends State<Mainview> { // 侧边缩略栏滚动控制器 final ScrollController _thumbController = ScrollController(); // 主视图滚动控制器 final ScrollController _mainController = ScrollController(); // 联动锁,避免滚动回调互相触发死循环 bool _isControlling = false; @override void initState() { super.initState(); // 监听缩略栏滚动 _thumbController.addListener(() { if (_isControlling) return; _isControlling = true; // 按比例计算主视图应该滚动的位置 final ratio = _thumbController.offset / _thumbController.position.maxScrollExtent; _mainController.jumpTo(ratio * _mainController.position.maxScrollExtent); _isControlling = false; }); // 监听主视图滚动 _mainController.addListener(() { if (_isControlling) return; _isControlling = true; // 按比例计算缩略栏应该滚动的位置 final ratio = _mainController.offset / _mainController.position.maxScrollExtent; _thumbController.jumpTo(ratio * _thumbController.position.maxScrollExtent); _isControlling = false; }); } @override void dispose() { _thumbController.dispose(); _mainController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Container( color: Colors.white.withOpacity(0.7), child: Row( children: [ // 侧边缩略栏 Expanded( flex: 1, child: ListView.separated( controller: _thumbController, primary: false, itemBuilder: (context, index) { return Padding( padding: const EdgeInsets.all(15), child: SizedBox( height: 150, child: CustomPage(index: index), ), ); }, separatorBuilder: (context, index) => const SizedBox(height: 3), itemCount: 5, ), ), // 主视图 Expanded( flex: 9, child: ListView.separated( controller: _mainController, primary: false, itemBuilder: (context, index) { return Padding( padding: const EdgeInsets.all(15), child: SizedBox( height: 850, child: CustomPage(index: index), ), ); }, separatorBuilder: (context, index) => const SizedBox(height: 3), itemCount: 5, ), ), ], ), ); } } // 示例CustomPage组件,可根据自己的业务修改 class CustomPage extends StatelessWidget { final int index; const CustomPage({Key? key, required this.index}) : super(key: key); @override Widget build(BuildContext context) { return Container( color: Colors.primaries[index % Colors.primaries.length].shade100, child: Center(child: Text('第$index页')), ); } }
可选优化
如果需要更精准的按索引对齐效果,可以在滚动监听中判断当前可见的第一个索引,直接滚动到对应索引的位置即可,比按比例计算的对齐效果更贴合记事本的使用场景。
内容的提问来源于stack exchange,提问作者Anas-Qasem
相关产品推荐
相关产品推荐

