Flutter中如何在ListView内嵌套多个ListView.builder实现嵌套滚动
问题解答
核心问题答复
- 同一Widget完全支持使用多个ScrollController,你可以为每一个内部
ListView.builder分配独立的ScrollController实例,互不干扰。 - 你需要的嵌套滚动联动效果可实现,推荐使用滚动通知监听方案,代码改造成本最低。
实现步骤
- 声明所有滚动控制器,注意在dispose生命周期销毁避免内存泄漏
// 声明控制器 late ScrollController _mainScrollController; late ScrollController _innerController1; late ScrollController _innerController2; late ScrollController _innerController3; @override void initState() { super.initState(); // 初始化控制器 _mainScrollController = ScrollController(); _innerController1 = ScrollController(); _innerController2 = ScrollController(); _innerController3 = ScrollController(); } @override void dispose() { // 销毁控制器 _mainScrollController.dispose(); _innerController1.dispose(); _innerController2.dispose(); _innerController3.dispose(); super.dispose(); }
- 改造嵌套ListView结构,给内部列表套
NotificationListener监听越界滚动事件,同时指定内部列表的固定高度
Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('AppBar'), ), body: ListView( controller: _mainScrollController, children: <Widget>[ // 第一个内部列表 SizedBox( height: 300, // 必须指定明确高度,按需调整 child: NotificationListener<OverscrollNotification>( onNotification: (notification) { // 内部列表滚动越界时,将偏移量转给外层主控制器 _mainScrollController.position.correctPixels( _mainScrollController.offset + notification.overscroll ); _mainScrollController.position.notifyListeners(); return false; }, child: ListView.builder( controller: _innerController1, itemCount: 20, itemBuilder: (context, index) => ListTile(title: Text('列表1 第$index项')), ), ), ), // 第二个内部列表,逻辑同上 SizedBox( height: 300, child: NotificationListener<OverscrollNotification>( onNotification: (notification) { _mainScrollController.position.correctPixels( _mainScrollController.offset + notification.overscroll ); _mainScrollController.position.notifyListeners(); return false; }, child: ListView.builder( controller: _innerController2, itemCount: 20, itemBuilder: (context, index) => ListTile(title: Text('列表2 第$index项')), ), ), ), // 第三个内部列表,逻辑同上 SizedBox( height: 300, child: NotificationListener<OverscrollNotification>( onNotification: (notification) { _mainScrollController.position.correctPixels( _mainScrollController.offset + notification.overscroll ); _mainScrollController.position.notifyListeners(); return false; }, child: ListView.builder( controller: _innerController3, itemCount: 20, itemBuilder: (context, index) => ListTile(title: Text('列表3 第$index项')), ), ), ), ], ) ); }
注意事项
- 内部ListView必须指定明确的高度,否则外层滚动容器无法确定子元素尺寸,会触发布局异常。
- 不要给内部ListView设置
shrinkWrap: true,该属性会让内部列表高度适配内容,失去独立滚动能力。 - 如果内部列表内容不足以撑满自身高度,它本身就无法触发滚动,滑动操作会直接由外层列表接管,属于正常表现。
内容的提问来源于stack exchange,提问作者Kumaresh Chandra Baruri
相关产品推荐
相关产品推荐

