在 Flutter 中,实现拖拽功能主要依赖于 Draggable 和 DragTarget 这两个核心组件。Draggable 用于定义可被拖动的"拖拽源",而 DragTarget 用于定义可以接收拖拽元素的"拖拽目标"。
1. Draggable(拖拽源)
Draggable 允许用户通过手势移动组件,并在拖动过程中携带数据。它有几个核心属性:
child:正常状态下显示的组件。feedback:拖拽过程中跟随手指移动的组件(通常设置为半透明或带有阴影的副本)。childWhenDragging:拖拽开始后,原位置留下的占位组件(通常设置为灰色或半透明)。也可以不写,这里一般会做半透明效果。data:拖拽时携带的数据,可以是任意类型,传递给DragTarget。axis:限制拖拽方向,可设置为水平(Axis.horizontal)或垂直(Axis.vertical)。不写表示任意方向。
2. DragTarget(拖拽目标)
DragTarget 是接收拖拽元素的区域。当 Draggable 被拖入该区域时,会触发相应的回调。核心属性包括:收到的数据是一个DragTargetDetails对象。里面有offset,data(就是传递过来的参数)
builder:构建目标区域的 UI,可以根据当前是否有拖拽元素进入来改变外观6。onWillAcceptWithDetails废弃的方法是onWillAccept:判断是否接受当前拖拽的数据,返回true表示接受。onAcceptWithDetails废弃的方法onAccept当用户松手且onWillAcceptWithDetails返回true时触发,处理接收到的数据
scala
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(home: HomePage()));
}
class HomePage extends StatefulWidget {
HomePage({Key? key}) : super(key: key);
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
Offset offset = Offset.zero;
var param = '';
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
color: Colors.blue,
child: Stack(
children: [
Positioned(
left: 100,
top: 100,
child: Draggable(
// 传递给DragTarget的数据
data: '123',
// 拖拽过程中显示的组件
feedback: Container(width: 100, height: 100, color: Colors.red),
// 正常状态下显示的组件
childWhenDragging: Container(
width: 200,
height: 200,
color: Colors.red.withValues(alpha: 0.5),
),
onDragUpdate: (DragUpdateDetails details) {
setState(() {
offset = details.delta;
});
},
child: Container(width: 200, height: 200, color: Colors.green),
),
),
Positioned(
right: 20,
bottom: 200,
width: 200,
height: 200,
child: DragTarget(
builder: (context, candidateData, rejectedData) {
return Container(
width: 200,
height: 200,
color: Colors.yellow,
child: Text(param),
);
},
onAcceptWithDetails: (DragTargetDetails data) {
setState(() {
param = data.data;
});
},
onWillAcceptWithDetails: (data) {
return true;
},
),
),
],
),
),
);
}
}
class SecondPage extends StatefulWidget {
SecondPage({Key? key}) : super(key: key);
@override
_SecondPageState createState() => _SecondPageState();
}
class _SecondPageState extends State<SecondPage> {
@override
Widget build(BuildContext context) {
return Scaffold(body: Container(child: null, color: Colors.red));
}
}