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回调中数据类型不匹配,同时自身判断逻辑因传递对象不是组件实例而失效。
修改方案如下:
- 为
Draggable和DragTarget指定一致的泛型类型,明确数据传递的类型 - 给
Draggable的data参数赋值为当前组件的widget实例,确保拖拽传递的是组件本身 - 保留自身判断逻辑,避免拖拽源被自身的
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
相关产品推荐
相关产品推荐

