拖拽Draggable容器至DragTarget时如何获取鼠标位置?
解决Flutter DragTarget内按落点区域执行不同操作的问题
不用拆分多个DragTarget,也不需要额外的MouseRegion或Listener,DragTarget自带的回调参数就能满足需求:
onWillAcceptWithDetails和onAcceptWithDetails回调接收的DragTargetDetails对象里,包含了拖拽落点相对于当前DragTarget的偏移量(offset)。结合DragTarget的尺寸,就能直接判断落点处于目标容器的哪个区域,完全避免拆分大量DragTarget带来的性能开销。
具体实现步骤
- 在
DragTarget的回调中获取DragTargetDetails.offset,该值是鼠标落点相对于DragTarget左上角的坐标 - 用
offset.dy和DragTarget的高度对比,判断是上半区还是下半区 - 根据判断结果执行对应操作
修改后的示例代码
import 'package:flutter/material.dart'; class DragDrop extends StatefulWidget { const DragDrop({super.key}); @override State<DragDrop> createState() => _DragDropState(); } class _DragDropState extends State<DragDrop> { Color dropColor = Colors.blue; Color upperTextColor = Colors.black; Color downTextColor = Colors.black; @override Widget build(BuildContext context) { return Stack( children: [ Positioned( top: 0, left: 0, child: Draggable<int>( data: 0, feedback: panel(), child: panel(), onDragCompleted: () { print('completed'); }, onDragStarted: () { print('dragging'); }, ), ), Positioned( top: 0, left: 500, child: DragTarget<int>( builder: (BuildContext context, List<dynamic> accepted, List<dynamic> rejected,) { return Container( color: dropColor, width: 200, height: 200, child: LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { return Stack( children: [ Positioned( top: 0, left: constraints.maxWidth / 2 - 40, child: Text( 'Upper side', style: TextStyle(fontSize: 15, color: upperTextColor), ), ), Positioned( top: constraints.maxHeight - 20, left: constraints.maxWidth / 2 - 40, child: Text( 'Down side', style: TextStyle(fontSize: 15, color: downTextColor), ), ), ], ); }), ); }, onWillAcceptWithDetails: (DragTargetDetails<int> details) { dropColor = Colors.blueAccent; // 实时判断落点区域并更新UI反馈 final targetHeight = details.context.size?.height ?? 200; if (details.offset.dy < targetHeight / 2) { upperTextColor = Colors.yellow; downTextColor = Colors.black; } else { downTextColor = Colors.yellow; upperTextColor = Colors.black; } setState(() {}); return true; }, onAcceptWithDetails: (DragTargetDetails<int> details) { print('dropped at offset: ${details.offset}'); final targetHeight = details.context.size?.height ?? 200; // 确认落点区域并执行最终操作 if (details.offset.dy < targetHeight / 2) { print('落在上半区'); } else { print('落在下半区'); } setState(() { dropColor = Colors.blue; }); }, onLeave: (data) { print('onLeave'); setState(() { dropColor = Colors.blue; upperTextColor = Colors.black; downTextColor = Colors.black; }); }, ), ), ], ); } Widget panel(){ return Container( color: Colors.red, width: 200, height: 200, ); } }
方案优势
- 仅需一个
DragTarget,无论划分多少个区域(哪怕40个),都只需通过计算offset区分,不会增加Widget树复杂度,避免性能问题 - 可在
onWillAcceptWithDetails中实时反馈落点区域的UI变化,提升交互体验 - 无需依赖其他事件监听Widget,逻辑更集中
内容的提问来源于stack exchange,提问作者Alexandre B.
相关产品推荐
相关产品推荐

