如何实现两个可互相拖放且不遮挡原组件的Flutter Widget?
解决Flutter里DragTarget不挡组件还能触发drop的问题
问题根源&解决思路
你遇到的问题本质是DragTarget的默认命中测试行为会把下方组件的触摸事件挡住。只要给DragTarget加上behavior: HitTestBehavior.translucent,就能让它既接收拖放事件,又不影响下面Draggable/Container的拖动触发。
完整可运行代码
import 'package:flutter/material.dart'; void main() => runApp(const DragDropDemo()); class DragDropDemo extends StatefulWidget { const DragDropDemo({super.key}); @override State<DragDropDemo> createState() => _DragDropDemoState(); } class _DragDropDemoState extends State<DragDropDemo> { // 用这个列表存容器颜色,拖放后交换位置更新UI List<Color> _containerColors = [Colors.red, Colors.blue]; @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar(title: const Text('拖放排序示例')), body: Padding( padding: const EdgeInsets.all(20.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: List.generate(_containerColors.length, (index) { final color = _containerColors[index]; // 每个位置用Stack把Draggable和DragTarget叠在一起 return Stack( alignment: Alignment.center, children: [ // 可拖动的彩色容器 Draggable<Color>( data: color, // 拖动时显示的半透明反馈 feedback: Container( width: 150, height: 150, color: color.withOpacity(0.7), ), // 拖动原位置显示的占位 childWhenDragging: Container( width: 150, height: 150, color: Colors.grey.withOpacity(0.5), ), // 正常状态的容器 child: Container( width: 150, height: 150, color: color, ), ), // 接收拖放的目标层 DragTarget<Color>( // 关键:设置这个属性,不挡下面的组件还能接收拖放 behavior: HitTestBehavior.translucent, // 拖放成功时触发,交换颜色位置更新UI onAccept: (droppedColor) { setState(() { final currentIdx = _containerColors.indexOf(color); final dropIdx = _containerColors.indexOf(droppedColor); // 交换两个颜色的位置 _containerColors[currentIdx] = droppedColor; _containerColors[dropIdx] = color; }); }, builder: (context, candidateData, rejectedData) { // 透明容器,尺寸和上面的彩色容器完全一致 return Container( width: 150, height: 150, color: Colors.transparent, ); }, ), ], ); }), ), ), ), ); } }
关键细节解释
- HitTestBehavior.translucent:这是解决问题的核心。默认情况下DragTarget是
opaque(不透明),会完全挡住下面的组件,导致Draggable拖不动。设置成translucent后,DragTarget自己能接收到拖放事件,同时下面的Draggable依然能触发拖动。 - Stack层级:把DragTarget放在Draggable上面,因为尺寸完全一致,所以拖放的命中区域刚好覆盖容器,不会错位。
- 排序逻辑:在
onAccept里通过setState交换两个颜色的位置,触发UI更新完成排序。
额外提示
如果你的容器里有按钮之类的交互组件,这个方法同样适用,translucent不会阻挡这些子组件的点击事件。
内容的提问来源于stack exchange,提问作者Alexandre B.
相关产品推荐
相关产品推荐

