Flutter中如何为ListWheelScrollView扩展长按、拖拽等手势事件?
给ListWheelScrollView的Item扩展长按、拖拽手势的解决方案
我想实现从ListWheelScrollView中拖拽Item(Point组件),但这个组件只提供了onSelectedItemChanged方法,请问能不能给它扩展onLongPress、onVerticalDrag这类手势事件?
我之前试过在选中项上方加一个能响应长按/拖拽的Point组件,但因为滚动的依赖问题,两者位置没法始终对齐,相关代码如下:
class _Points extends StatelessWidget { int x; int selected; _Points(this.x, this.selected); @override Widget build(BuildContext context) { return Stack(children: <Widget>[ Container( width: x == selected ? 60 : 50, height: x == selected ? 60 : 50, child: SvgPicture.asset("svg/ball.svg"), ), RotatedBox( quarterTurns: 1, child: AnimatedContainer( duration: Duration(milliseconds: 0), width: x == selected ? 60 : 50, height: x == selected ? 60 : 50, alignment: Alignment.center, child: Text('$x', textScaleFactor: x == selected ? 2 : 1, style: TextStyle(fontWeight: FontWeight.bold)))) ]); } } class _MyHomePageState extends State<MyHomePage> { double itemWidth = 60.0; int itemCount = 20; int selected = 5; int scrollHeight = 380; int scrollwidth = 200; _Points selectedPoint = _Points(0, 0); FixedExtentScrollController _scrollController = FixedExtentScrollController(initialItem: 5); @override void initState() { super.initState(); selectedPoint = _Points(selected, selected); _scrollController = FixedExtentScrollController(initialItem: selected); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("SAGWEGEWG"), ), body: Column(children: [ RotatedBox( quarterTurns: -1, child: Stack(children: <Widget>[ Container( color: Colors.amber, height: scrollHeight.toDouble(), width: scrollwidth.toDouble(), child: ListWheelScrollView( onSelectedItemChanged: (x) { setState(() { selected = x; }); selectedPoint = _Points(x, x); print(selectedPoint.x); }, useMagnifier: false, controller: _scrollController, itemExtent: itemWidth, children: List.generate( itemCount, (x) => _Points(x, selected), )), ), Positioned( top: (scrollHeight - itemWidth + 10) / 2, left: (scrollwidth - itemWidth + 10) / 2, child: GestureDetector( child: selectedPoint, onLongPress: () { print("Pressed item $selected"); }, ), ) ])) ]), ); } }
直接给ListWheelScrollView的每个Item包裹GestureDetector是更可靠的方案,不用额外叠加单独的手势层,从根源避免位置对齐问题。
具体修改思路
- 给每个
_Points组件外层套上GestureDetector,直接在Item内部处理手势事件 - 若需区分选中状态的手势逻辑,可结合
selected参数做条件判断 - 拖拽功能可通过
Draggable+DragTarget组合实现,或利用GestureDetector的onVerticalDrag系列回调自定义逻辑
修改后的示例代码
class _Points extends StatelessWidget { final int x; final int selected; final VoidCallback? onLongPress; final Function(DragStartDetails)? onVerticalDragStart; _Points(this.x, this.selected, {this.onLongPress, this.onVerticalDragStart}); @override Widget build(BuildContext context) { return GestureDetector( // 仅给选中项绑定手势的话,可加条件:onLongPress: x == selected ? (){...} : null onLongPress: onLongPress, onVerticalDragStart: onVerticalDragStart, child: Stack(children: <Widget>[ Container( width: x == selected ? 60 : 50, height: x == selected ? 60 : 50, child: SvgPicture.asset("svg/ball.svg"), ), RotatedBox( quarterTurns: 1, child: AnimatedContainer( duration: Duration(milliseconds: 0), width: x == selected ? 60 : 50, height: x == selected ? 60 : 50, alignment: Alignment.center, child: Text('$x', textScaleFactor: x == selected ? 2 : 1, style: TextStyle(fontWeight: FontWeight.bold)))) ]), ); } } class _MyHomePageState extends State<MyHomePage> { double itemWidth = 60.0; int itemCount = 20; int selected = 5; int scrollHeight = 380; int scrollwidth = 200; FixedExtentScrollController _scrollController = FixedExtentScrollController(initialItem: 5); @override void initState() { super.initState(); _scrollController = FixedExtentScrollController(initialItem: selected); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("SAGWEGEWG"), ), body: Column(children: [ RotatedBox( quarterTurns: -1, child: Container( color: Colors.amber, height: scrollHeight.toDouble(), width: scrollwidth.toDouble(), child: ListWheelScrollView( onSelectedItemChanged: (x) { setState(() { selected = x; }); print(selected); }, useMagnifier: false, controller: _scrollController, itemExtent: itemWidth, children: List.generate( itemCount, (x) => _Points( x, selected, onLongPress: () { print("长按了Item $x"); }, onVerticalDragStart: (details) { print("开始拖拽Item $x"); // 此处可添加自定义拖拽逻辑 }, ), )), )) ]), ); } }
说明
- 移除了原方案中叠加的
selectedPoint和Positioned层,直接给每个Item绑定手势 - 手势事件直接触发在对应Item上,无位置偏移问题,支持所有Item的手势响应
- 若仅需给选中Item添加手势,可在
GestureDetector的回调中加入x == selected的条件判断
内容的提问来源于stack exchange,提问作者Mihai Rosu
相关产品推荐
相关产品推荐

