【Flutter 专题】96 图解 Draggable + DragTarget 基本拖拽效果
时间:2022-07-22
本文章向大家介绍【Flutter 专题】96 图解 Draggable + DragTarget 基本拖拽效果,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
和尚尝试做一个新闻类 app 常见的可以滑动添加和删除 item 选项卡的小功能,和尚尝试采用 Draggable + DragTarget 方式;今天先学习一下 Draggable 拖拽组件的基本应用;
Draggable
源码分析
const Draggable({
Key key,
@required this.child,
@required this.feedback,
this.data,
this.axis,
this.childWhenDragging,
this.feedbackOffset = Offset.zero,
this.dragAnchor = DragAnchor.child,
this.affinity,
this.maxSimultaneousDrags,
this.onDragStarted,
this.onDraggableCanceled,
this.onDragEnd,
this.onDragCompleted,
this.ignoringFeedbackSemantics = true,
})
分析源码可得,Draggable 是有状态的 StatefulWidget 组件,一般与 DragTarget 配合使用,拖拽至 DragTarget;其中 child 和 feedback 是两个必填属性,分别代表默认情况展示的子 Widget 和拖拽过程中移动时的子 Widget;
案例尝试
- 和尚先尝试一个最基本的 Draggable 效果,然后逐步添加属性效果;
Draggable(
child: Image.asset('images/icon_hzw01.jpg', width: 150.0),
feedback: Image.asset('images/icon_hzw02.jpg', width: 150.0));
- affinity 属性主要是用于与其他手势竞争,例如在垂直列表中,且 affinity 设置 Axis.horizontal 水平属性,则只允许水平方向拖拽,竖直方向则是列表的滚动;
return ListView(children: <Widget>[
Icon(Icons.access_alarm, size: 100),
Icon(Icons.print, size: 100),
Icon(Icons.android, size: 100),
Draggable(
child: Icon(Icons.ac_unit, size: 150, color: Colors.blue),
feedback: Icon(Icons.ac_unit, size: 200, color: Colors.red),
affinity: Axis.horizontal),
Icon(Icons.directions_car, size: 100),
Icon(Icons.sync, size: 100),
Icon(Icons.error, size: 100),
Icon(Icons.send, size: 100),
Icon(Icons.call, size: 100)
]);
- axis 用于限制拖拽方向,水平或竖直方向,若设置 null 则是全方向拖拽;其中在与其他滑动手势冲突时与 affinity 配合使用;
Draggable(affinity: Axis.horizontal, axis: Axis.horizontal,
child: Image.asset('images/icon_hzw01.jpg', width: 150.0),
feedback: Image.asset('images/icon_hzw02.jpg', width: 150.0));
- childWhenDragging 为拖拽过程中,原位置子 Widget 对应展示内容;
Draggable(affinity: Axis.horizontal, axis: null,
child: Image.asset('images/icon_hzw01.jpg', width: 150.0),
feedback: Image.asset('images/icon_hzw02.jpg', width: 150.0),
childWhenDragging: Image.asset('images/icon_hzw03.jpg', width: 150.0));
- dragAnchor 为移动过程中锚点位置,分为 child 和 pointer 两种;child 是以默认子 child 为基础,起始点以 Offset.zero 左上角位置为准;pointer 以在子 child 范围内,手势点击时位置为准;
Draggable(affinity: Axis.horizontal, axis: null,
child: Image.asset('images/icon_hzw01.jpg', width: 150.0),
feedback: Image.asset('images/icon_hzw02.jpg', width: 150.0),
dragAnchor: DragAnchor.pointer);
- maxSimultaneousDrags 为针对于同一个子 child 可以同时拖拽个数,和尚尝试的两个手指同时向两个方向拖拽;
Draggable(affinity: Axis.horizontal, axis: null,
child: Image.asset('images/icon_hzw01.jpg', width: 150.0),
feedback: Image.asset('images/icon_hzw02.jpg', width: 150.0),
maxSimultaneousDrags: 2);
- ignoringFeedbackSemantics 当子 child 和 feedback 为同一个 Widget 时,可以通过 ignoringFeedbackSemantics 设为 false 配合 Key 确保是同一个 Widget 减少绘制;
Draggable(affinity: Axis.horizontal, axis: null,
child: Image.asset('images/icon_hzw01.jpg', width: 150.0, key: _itemKey),
feedback: Image.asset('images/icon_hzw01.jpg', width: 150.0),
ignoringFeedbackSemantics: false);
- onDraggableX 为拖拽过程中的回调函数;onDragStarted 为开始拖拽时回调;onDraggableCanceled 为在没有被 DragTarget 接收时取消的回调;onDragEnd 为拖拽结束时的回调,不管是否被 DragTarget 接收;onDragCompleted 为被 DragTarget 接收成功时回调;
Draggable(affinity: Axis.horizontal, axis: null,
child: Image.asset('images/icon_hzw01.jpg', width: 150.0, key: _itemKey),
feedback: Image.asset('images/icon_hzw01.jpg', width: 150.0),
childWhenDragging: Container(),
onDragCompleted: () => print('Draggable --> onDragCompleted'),
onDragEnd: (DraggableDetails details) => print('Draggable --> onDragEnd --> ${details.offset}'),
onDraggableCanceled: (Velocity velocity, Offset offset) => print('Draggable --> onDraggableCanceled --> $offset'),
onDragStarted: () => print('Draggable --> onDragStarted'));
- data 为 T 任意类型数据,主要是向 DragTarget 传递;
data: 'Draggable Data A !!!',
DragTarget
源码分析
const DragTarget({
Key key,
@required this.builder,
this.onWillAccept,
this.onAccept,
this.onLeave,
})
分析源码可得 DragTarget 同样为 StatefulWidget 带状态的 Widget,其中 builder 构造器为必填属性,用于构建接收 Draggable 后的 Widget 构建;
案例尝试
- builder 为构造器,其中包括三个属性,分别为 context 上下文环境,candidateData 为 onWillAccept 回调为 true 时可接收的数据列表,rejectedData 为 onWillAccept 回调为 false 时拒绝时的数据列表;
- onWillAccept 为拖拽到 DragTarget 时的回调,true 时会将 Data 数据添加到 candidateData 列表中;false 时会将 Data 数据添加到 rejectedData 列表中;
- onAccept 用于接收 Data 数据;
- onLeave 为离开时的回调;且和尚测试过程中,当 onWillAccept 返回 true 时,onAccept 和 onLeave 临界为手势拖拽的最后的坐标是否在 DragTarget 范围内;
DragTarget<String>(builder: (BuildContext context, List<String> candidateData, List<dynamic> rejectedData) {
print('DragTarget --> builder --> $candidateData --> $rejectedData -->$_dragState');
return _dragState
? Image.asset('images/icon_hzw01.jpg', width: 150.0)
: Container(height: 150.0, width: 150.0, color: Colors.blue.withOpacity(0.4));
}, onAccept: (String data) {
print('DragTarget --> onAccept --> $data -->$_dragState');
setState(() {
_dragState = true;
});
}, onLeave: (String data) {
print('DragTarget --> onLeave --> $data');
}, onWillAccept: (String data) {
print('DragTarget --> onWillAccept --> $data');
return true;
});
LongPressDraggable
源码分析
const LongPressDraggable({
Key key,
@required Widget child,
@required Widget feedback,
T data,
Axis axis,
Widget childWhenDragging,
Offset feedbackOffset = Offset.zero,
DragAnchor dragAnchor = DragAnchor.child,
int maxSimultaneousDrags,
VoidCallback onDragStarted,
DraggableCanceledCallback onDraggableCanceled,
DragEndCallback onDragEnd,
VoidCallback onDragCompleted,
this.hapticFeedbackOnStart = true,
bool ignoringFeedbackSemantics = true,
})
分析源码可得,LongPressDraggable 继承自 Draggable,属性和方法基本完全一致,只是需要长按拖拽;
Draggable + DragTarget 案例尝试
和尚简答尝试了 Draggable 拖拽 Widget 以及对应接收拖拽的 DragTarget,下节尝试新闻类类型选项卡;和尚对 Draggable 底层源码还不够熟悉,如有问题请多多指导!
来源:阿策小和尚
- Html5模拟通讯录人员排序(sen.js)
- Goroutine + Channel 实践
- Http和Https的区别
- JavaScript实现单击全选 ,再次点击取消全选
- 连AI都在看《英雄联盟》游戏直播
- MAC使用adb工具
- 并发编程之master-worker模式
- Android WebView全面总结
- Url参数中出现+、空格、=、%、&、#等字符的解决办法
- 解决CSS垂直居中的几种方法(基于绝对定位,基于视口单位,Flexbox方法)
- Go语言学习之cgo(golang与C语言相互调用)
- golang之旅--数据类型之字符串
- Android保存图片到系统图库
- 基于Vue.js的大型报告页项目实现过程及问题总结(二)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- I/O多路复用器之隐秘的角落
- 打卡群刷题总结0809——二叉树的锯齿形层次遍历
- 简单的ssm整合练手项目:汽车项目
- 在spring-boot中使用pageHelper插件
- 要深入 JavaScript,你需要掌握这 36 个概念
- mybatis-plus实现增删改查
- mybatis-plus代码生成器
- mybatis-plus逻辑删除
- mybatis-plus一些关键配置
- mybatis-plus自定义sql注入器
- k8s代码走读---kube-controller-manager
- 我们一起学一学渗透测试——黑客应该掌握的HTML基础知识(一)
- 一套漏洞组合拳接管你的账号
- 我们一起学一学渗透测试——黑客应该掌握的HTML基础知识(二)
- 我用Paddle Lite在树莓派3b+上从零开始搭建“实时表情识别”项目