Flutter嵌套滚动组件时ListWheelScrollView无法滚动的解决方法
问题描述
将ListWheelScrollView作为垂直滚动的SingleChildScrollView的子组件,通过RotatedBox将其转为水平滚动样式。但默认情况下,ListWheelScrollView无法接收滚动手势,只有把父组件SingleChildScrollView的physics设置为NeverScrollableScrollPhysics()时,才能正常滚动ListWheelScrollView。需要在保留父组件垂直滚动能力的同时,让ListWheelScrollView正常工作,且无需自定义组件。
复现代码
import 'package:flutter/material.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( home: Material( color: Colors.transparent, child: SingleChildScrollView( child: Column( children: [ Container(height: 300, color: Colors.black), Container(height: 300, color: Colors.red), Container(height: 300, color: Colors.green), Container( height: 300, child: RotatedBox( quarterTurns: 1, child: ListWheelScrollView( itemExtent: 25, children: const [ Text("something"), Text("something"), Text("something"), Text("something"), Text("something"), Text("something"), ], ), ), color: Colors.white, ), Container(height: 300, color: Colors.blue), Container(height: 300, color: Colors.orange), ], ), ), ), ); } }
问题现象:滚动到ListWheelScrollView区域时,无法触发其滚动操作;若将SingleChildScrollView的
physics设为NeverScrollableScrollPhysics(),ListWheelScrollView可正常滚动,但父组件失去垂直滚动能力。
解决方案
方案1:用GestureDetector拦截垂直手势
给ListWheelScrollView的父容器包裹GestureDetector,拦截垂直方向的拖拽事件,阻止父组件的SingleChildScrollView抢占手势,让ListWheelScrollView能正常接收旋转后的滚动手势。
修改后的核心代码:
Container( height: 300, color: Colors.white, child: GestureDetector( // 拦截垂直拖拽事件,避免父滚动视图响应 onVerticalDragStart: (_) {}, onVerticalDragUpdate: (_) {}, child: RotatedBox( quarterTurns: 1, child: ListWheelScrollView( itemExtent: 25, children: const [ Text("something"), Text("something"), Text("something"), Text("something"), Text("something"), Text("something"), ], ), ), ), ),
方案2:用NotificationListener阻止事件冒泡
通过NotificationListener捕获ListWheelScrollView的滚动事件,阻止事件传递给父组件的SingleChildScrollView。
核心代码示例:
Container( height: 300, color: Colors.white, child: NotificationListener<ScrollNotification>( onNotification: (notification) { // 返回true表示事件已被处理,不再向上传递 return true; }, child: RotatedBox( quarterTurns: 1, child: ListWheelScrollView( itemExtent: 25, children: const [ Text("something"), Text("something"), Text("something"), Text("something"), Text("something"), Text("something"), ], ), ), ), ),
原理说明
RotatedBox将ListWheelScrollView旋转90度后,其原本的水平滚动手势在视觉上对应垂直方向的拖拽。而父组件SingleChildScrollView默认会优先拦截垂直拖拽事件,导致ListWheelScrollView无法接收手势。上述两种方案通过拦截手势或阻止事件冒泡,让ListWheelScrollView能正常响应滚动操作,同时保留父组件的垂直滚动能力。
内容的提问来源于stack exchange,提问作者Tariq_J99

