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

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是更可靠的方案,不用额外叠加单独的手势层,从根源避免位置对齐问题。

具体修改思路

  1. 给每个_Points组件外层套上GestureDetector,直接在Item内部处理手势事件
  2. 若需区分选中状态的手势逻辑,可结合selected参数做条件判断
  3. 拖拽功能可通过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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 08:48:12