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

Draggable包裹DragTarget时onMove抛异常,如何实现组件兼具两者功能?

实现兼具Draggable与DragTarget功能的组件问题解决

问题场景

使用如下方式嵌套DraggableDragTarget组件时:

Stack(
  children: [
    Positioned( top: 100, left: 100, child: DraggableDragTarget( title: 'one' )),
    Positioned( top: 300, left: 300, child: DraggableDragTarget( title: 'other' ))
  ],
 )

当为组件内的DragTarget实现onMove属性时,拖拽操作会立即抛出异常并终止;移除onMove则拖拽正常执行。虽已在onWillAccept中添加判断,拒绝拖拽源为自身的情况,但问题仍未解决。

问题代码

import 'package:flutter/material.dart';

class DraggableDragTarget extends StatefulWidget {
  DraggableDragTarget({Key? key, required this.title}) : super(key: key);
  String title;

  @override
  State<DraggableDragTarget> createState() => _DraggableDragTargetState();
}

class _DraggableDragTargetState extends State<DraggableDragTarget> {
  @override
  Widget build(BuildContext context) {
    Widget inner = DragTarget(
      builder: (context, candidateData, rejectedData) =>
          Container(child: Text(widget.title, style: TextStyle(fontSize: 20))),
      onAccept: (something) {},
      onLeave: (something) {},

      // 定义此回调会导致拖拽抛出异常并立即终止
      onMove: (someDragTargetDetail) {},

      onWillAccept: (something) {
        if (null == something) {
          return false;
        }
        print(widget.toString() + ' vs. ' + something.toString());
        // 判断是否为自身拖拽源
        if (something == widget) {
          return false;
        } else {
          return true;
        }
      },
    );

    Widget feedback = Text('Feedback placeholder',
        style: TextStyle(fontSize: 20, color: Colors.green));

    return Draggable(child: inner, feedback: feedback);
  }
}

问题原因与解决方案

问题核心在于Draggable未指定data参数时,默认传递的是内部DragTarget实例,导致onMove回调中数据类型不匹配,同时自身判断逻辑因传递对象不是组件实例而失效。

修改方案如下:

  1. 为Draggable和DragTarget指定一致的泛型类型,明确数据传递的类型
  2. 给Draggable的data参数赋值为当前组件的widget实例,确保拖拽传递的是组件本身
  3. 保留自身判断逻辑,避免拖拽源被自身的DragTarget接收

修改后的完整代码:

import 'package:flutter/material.dart';

class DraggableDragTarget extends StatefulWidget {
  DraggableDragTarget({Key? key, required this.title}) : super(key: key);
  final String title;

  @override
  State<DraggableDragTarget> createState() => _DraggableDragTargetState();
}

class _DraggableDragTargetState extends State<DraggableDragTarget> {
  @override
  Widget build(BuildContext context) {
    // 指定DragTarget泛型为组件自身类型,匹配Draggable的数据类型
    Widget inner = DragTarget<DraggableDragTarget>(
      builder: (context, candidateData, rejectedData) =>
          Container(
            padding: EdgeInsets.all(16),
            color: candidateData.isNotEmpty ? Colors.blue.shade100 : Colors.white,
            child: Text(widget.title, style: TextStyle(fontSize: 20)),
          ),
      onAccept: (DraggableDragTarget data) {
        print('接收拖拽:${data.title}');
      },
      onLeave: (DraggableDragTarget? data) {
        if (data != null) {
          print('离开目标:${data.title}');
        }
      },
      // 现在onMove可正常执行,无异常
      onMove: (DragTargetDetails<DraggableDragTarget> details) {
        print('拖拽经过:${details.data.title}');
      },
      onWillAccept: (DraggableDragTarget? data) {
        if (data == null) return false;
        // 拒绝自身作为拖拽源
        return data != widget;
      },
    );

    Widget feedback = Container(
      padding: EdgeInsets.all(16),
      color: Colors.green.shade100,
      child: Text('正在拖拽 ${widget.title}', style: TextStyle(fontSize: 20, color: Colors.green)),
    );

    // 指定Draggable的data为当前组件实例,泛型与DragTarget一致
    return Draggable<DraggableDragTarget>(
      data: widget,
      child: inner,
      feedback: feedback,
    );
  }
}

关键修改点

  • 为Draggable和DragTarget添加一致的泛型类型,避免类型不匹配异常
  • 给Draggable设置data: widget,确保拖拽传递的是组件实例而非内部子组件
  • 优化onWillAccept判断逻辑,准确识别自身拖拽源
  • 为builder添加视觉反馈,直观区分拖拽状态

内容的提问来源于stack exchange,提问作者SteAp

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 18:16:08