前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Flutter 组件集录】Draggable 与 DragTarget

【Flutter 组件集录】Draggable 与 DragTarget

作者头像
张风捷特烈
发布2022-03-18 15:53:21
7780
发布2022-03-18 15:53:21
举报
一、认识 Draggable 组件

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

代码语言:javascript
复制
final Widget child;
final Widget feedback;
1. 拖动的方向: axis

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

代码语言:javascript
复制
class CustomDraggable extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    List<Axis?> 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 属性指定在拖拽过程中原来位置的组件。如下,拖动时原来的位置显示为 橙色小圆 和 删除图标。

代码语言:javascript
复制
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数据。
代码语言:javascript
复制
List<Widget> _buildDraggable() {
  return colors.map(
        (Color color) => Draggable<Color>(
            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 支持多个同时拖动,使用是数据列表。

代码语言:javascript
复制
DragTarget<Color>(
  onLeave: _onLeave,
  onAccept: _onAccept,
  onWillAccept: _onWillAccept,
  builder: _buildTarget,
)
  
Widget _buildTarget(BuildContext context, List<Color?> 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 中可以获取拖入进的索引数据,从而实现删除功能。

代码语言:javascript
复制
class DeleteDraggable extends StatefulWidget {
  @override
  _DeleteDraggableState createState() => _DeleteDraggableState();
}

class _DeleteDraggableState extends State<DeleteDraggable> {
  List<Color> 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: <Widget>[
          Wrap(
            children: _buildDraggable(),
            spacing: 10,
          ),
          SizedBox(
            height: 20,
          ),
          DragTarget<int>(
              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<Widget> _buildDraggable() => colors
      .map((Color color) => Draggable<int>(
            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 的联合使用,不需要我们自己去实现拖拽逻辑,可以很轻松解决很多目标拖拽的问题。那本文到这里就结束了,谢谢观看,明天见~

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021/08/30 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、认识 Draggable 组件
  • 1. 拖动的方向: axis
  • 2.拖动时原位置组件: childWhenDragging
  • 二、Draggable 与 DragTarget 联合使用
    • 1. 综合测试案例
      • 2.拖拽删除案例
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档