【Flutter 组件集录】Draggable 与 DragTarget

前言:

这是我参与8月更文挑战的第 30 天,活动详情查看:8月更文挑战。为应掘金的八月更文挑战,我准备在本月挑选 31 个以前没有介绍过的组件,进行全面分析和属性介绍。这些文章将来会作为 Flutter 组件集录 的重要素材。希望可以坚持下去,你的支持将是我最大的动力~

| 本系列 | 组件文章 | 列表 | | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | | 1.NotificationListener | 2.Dismissible | 3.Switch | | 4.Scrollbar | 5.ClipPath | 6.CupertinoActivityIndicator | | 7.Opacity | 8.FadeTransition | 9. AnimatedOpacity | | 10. FadeInImage | 11. Offstage | 12. TickerMode | | 13. Visibility | 14. Padding | 15. AnimatedContainer | | 16.CircleAvatar | 17.PhysicalShape | 18.Divider | | 19.Flexible、Expanded 和 Spacer | 20.Card | 21.SizedBox | | 22.ConstrainedBox | 23.Stack | 24.Positioned | | 25.OverflowBox | 26.SizedOverflowBox | 27. DecoratedBox | | 28. BackdropFilter | 29.ImageFiltered 与 ColorFiltered | 30.Draggable 与 DragTarget |


一、认识 Draggable 组件

Draggable 顾名思义,是可拖动的组件,它继承自 StatefulWidget ,且可接受一个泛型。 构造方法有非常多的入参,其中必须传入的是 childfeedback 两个组件。

dart final Widget child; final Widget feedback;


1. 拖动的方向: axis

下面先通过一个小案例认识一下 Draggable:下面是三个 Draggable 组件,其中 child 是蓝色小圆,feedback 是红色小圆,三者的区别在于 axis 属性不同。左边 axisnull ,表示不限定轴向,可以自由拖动;中间 axisvertical ,只能在竖直方向拖动;中间 axishorizontal ,只能在水平方向拖动。

```dart class CustomDraggable extends StatelessWidget { @override Widget build(BuildContext context) { List axis = [null, Axis.vertical, Axis.horizontal]; return Wrap( spacing: 30, children: axis .map((Axis? axis) => Draggable( axis: axis, child: buildContent(), feedback: buildFeedback(), )) .toList()); }

Widget buildContent() { return Container( width: 30, height: 30, alignment: Alignment.center, decoration: BoxDecoration( color: Colors.blue, shape: BoxShape.circle, ), ); }

Widget buildFeedback() { return Container( width: 30, height: 30, decoration: BoxDecoration( color: Colors.red, shape: BoxShape.circle, ), ); } } ```


2.拖动时原位置组件: childWhenDragging

Draggable 可以通过 childWhenDragging 属性指定在拖拽过程中原来位置的组件。如下,拖动时原来的位置显示为 橙色小圆 和 删除图标。

```dart Draggable( axis: axis, childWhenDragging: buildWhenDragging(), child: buildContent(), feedback: buildFeedback(), ))

Widget buildWhenDragging() { return Container( width: 30, height: 30, decoration: BoxDecoration( color: Colors.orange, shape: BoxShape.circle, ), child: Icon( Icons.delete_outline, size: 20, color: Colors.white, ), ); } ```


二、Draggable 与 DragTarget 联合使用
1. 综合测试案例

下面通过一个示例测试一下 DraggableDragTarget 的联合使用。如下,上面的小球是 Draggable ,下面的区域是 DragTarget 。可以拖动小球来为 DragTarget 着色,并且显示当前操作的信息。

Draggable 可以监听五个回调:

  • onDragStarted :开始拖动时回调,无回调数据。
  • onDragEnd:结束拖动时回调,可以获取 DraggableDetails 数据。
  • onDragUpdate:拖动更新时回调,可以获取 DraggableDetails 数据。
  • onDragCompleted : 拖入目标区域,并松手完成时回调,无回调数据。
  • onDraggableCanceled:未在目标区域,拖拽取消回调,可以获取 VelocityOffset数据。

```dart List _buildDraggable() { return colors.map( (Color color) => Draggable ( onDragStarted: _onDragStarted, onDragEnd: _onDragEnd, onDragUpdate: _onDragUpdate, onDragCompleted: _onDragCompleted, onDraggableCanceled: _onDraggableCanceled, childWhenDragging: childWhenDragging(colors.indexOf(color).toString()), child: buildContent(color), data: color, feedback: buildFeedback(color)), ).toList(); }

void _onDragUpdate(DragUpdateDetails details) { print('坐标:' '(${details.localPosition.dx.toStringAsFixed(1)},' '${details.localPosition.dy.toStringAsFixed(1)})'); }

void _onDraggableCanceled(Velocity velocity, Offset offset) { _info = '拖拽取消'; }

void _onDragCompleted() { _info = '拖拽完成'; }

void _onDragEnd(DraggableDetails details) { setState(() => _info = '结束拖拽'); }

void _onDragStarted() { setState(() => _info = '开始拖拽'); } ```


DragTarget 会通过 builder 回调来构建组件,其中会回调 candidateDatarejectedData 两个列表,其中包含接受拒绝 的数据。由于 Draggable 支持多个同时拖动,使用是数据列表。

```dart DragTarget ( onLeave: _onLeave, onAccept: _onAccept, onWillAccept: _onWillAccept, builder: _buildTarget, )

Widget _buildTarget(BuildContext context, List candidateData, List rejectedData) { return Container( width: 150.0, height: 50.0, color: _color, child: Center( child: Text( _info, style: TextStyle(color: Colors.white), ), )); }

void _onLeave(Color? data) { print("onLeave: data = $data "); setState(() => _info = 'onLeave'); }

void _onAccept(Color data) { print("onAccept: data = $data "); setState(() => _color = data); }

bool _onWillAccept(Color? data) { print("onWillAccept: data = $data "); setState(() => _info = 'onWillAccept'); return data != null; } ```

onWillAcceptDragTarget 中比较重要的一个回调,当拖动的组件到达目标区域后,onWillAccept 会触发。从下面源码中可以看出 _candidateAvatars_rejectedAvatarsonWillAccept 的返回值有关。如果 onWillAccept 返回 false ,则数据会被简入到 _rejectedAvatars

builder 中的回调入参 candidateDatarejectedData 就是根据上面两个列表计算的。


2.拖拽删除案例

如下示例,通过拓展组件目标到指定位置进行移除,通过 Draggable 和 DragTarget 联合就很容易实现。

代码实现如下,通过颜色数组 colors 生成不同颜色的 Draggable ,并拥有 int 泛型,传递的数值为可拖拽组件的索引,这样在 DragTargetonAccept 中可以获取拖入进的索引数据,从而实现删除功能。

```dart class DeleteDraggable extends StatefulWidget { @override _DeleteDraggableState createState() => _DeleteDraggableState(); }

class _DeleteDraggableState extends State { List colors = [ Colors.red, Colors.yellow, Colors.blue, Colors.green, Colors.orange, Colors.purple, Colors.cyanAccent];

@override Widget build(BuildContext context) { return Column( mainAxisSize: MainAxisSize.min, children: [ Wrap( children: _buildDraggable(), spacing: 10, ), SizedBox( height: 20, ), DragTarget ( onAccept: _onAccept, onWillAccept: (data) => data != null, builder: buildTarget ) ], ); }

Widget buildTarget(context, candidateData, rejectedData) => Container( width: 40.0, height: 40.0, decoration: BoxDecoration(color: Colors.red, shape: BoxShape.circle), child: Center( child: Icon(Icons.delete_sweep, color: Colors.white), ));

List _buildDraggable() => colors .map((Color color) => Draggable ( child: buildContent(color), data: colors.indexOf(color), childWhenDragging: buildWhenDragging(), feedback: buildFeedback(color)), ).toList();

Widget buildContent(Color color) { return Container( width: 30, height: 30, alignment: Alignment.center, child: Text( colors.indexOf(color).toString(), style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold), ), decoration: BoxDecoration(color: color, shape: BoxShape.circle), ); }

Widget buildFeedback(Color color) { return Container( width: 25, height: 25, decoration: BoxDecoration(color: color.withAlpha(100), shape: BoxShape.circle), ); }

Widget buildWhenDragging() { return Container( width: 30, height: 30, decoration: BoxDecoration(color: Colors.red, shape: BoxShape.circle), child: Icon(Icons.delete_outline, size: 20, color: Colors.white, ), ); }

void _onAccept(int data) { setState(() { colors.removeAt(data); }); } } ```

通过Draggable 和 DragTarget 的联合使用,不需要我们自己去实现拖拽逻辑,可以很轻松解决很多目标拖拽的问题。那本文到这里就结束了,谢谢观看,明天见~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值