Flutter初探--可拖拽排序的九宫格

    本文通过可拖拽排序的九宫格对Draggable和DragTarget的组合使用进行了说明。组合使用即让DragTarget成为Draggable的child或间接child,这样便同时拥有了可拖拽和接收数据的功能。若对Draggble基础不太了解可以看:

《Flutter初探--Draggable基础篇》
《Git源码》

演示图例:

实例效果图


1 下午7.35.21.gif

回调顺序:

1 下午7.33.22.gif
拖到外边
flutter: === onWillAccept: Go ==> Go
flutter: === onDragStarted
flutter: === onLeave: Go ==> Go
flutter: === onDraggableCanceled

这里可以看到,当组件直接拖出九宫格后,回调的调用顺序是:
先经过自己 ==> 开始拖动 ==> 离开自己 ==> 最后接收失败取消
为什么会先经过自己呢???继续看下张图


1 下午7.26.39.gif
一次交换
flutter: === onWillAccept: OC ==> OC
flutter: === onDragStarted
flutter: === onLeave: OC ==> OC
flutter: === onWillAccept: OC ==> Python
flutter: === onLeave: OC ==> OC
flutter: === onWillAccept: OC ==> C
flutter: === onAccept: OC ==> OC
flutter: === onDragCompleted

这里是一次交换成功的回调日志:
效果上看:OC 经过 Python并进行了重新排序
回调上看:先经过自己 ==> 开始拖动 ==> 离开自己 ==> 经过别人 ==> 是否接收 ==> 接收 ==> 接收完成

同样的先经过自己,再离开自己
注意:这里的第5行和第7行的OC ==> OC是因为我们在移动过程中进行了数据交换


2.gif
两次交换
flutter: === onWillAccept: C++ ==> C++
flutter: === onDragStarted
flutter: === onLeave: C++ ==> C++
flutter: === onWillAccept: C++ ==> Python
flutter: === onLeave: C++ ==> C++
flutter: === onWillAccept: C++ ==> C
flutter: === onAccept: C++ ==> C++
flutter: === onDragCompleted

这里是一次交换成功的回调日志:
效果上看:C++ 经过 Python 重新排序 又经过 C 重新排序
回调不再赘述

对比上篇[《Flutter初探--Draggable基础篇》](https://www.jianshu.com/p/eb3b9e903579)的回调顺序可以看出,当Draggable与DragTarget组合使用时,因为自己既是拖动组件又是接收组件,所以会先调用onWillAccept及先经过自己等回调。

功能说明:

  1. 九宫格内所有组件均可拖动
  2. 九宫格内所有组件均可接收数据
  3. 组件拖拽后原位置变为空白
  4. 组件拖动过程中变大且为绿色带透明度
  5. 组件拖动过程中,经过的组件后移,后移后原位置空出
  6. 拖动结束后组件停留在当前位置

功能解析:

  1. 九宫格内所有组件均可拖动,说明所有item包含Draggable组件
  2. 九宫格内所有组件均可接收数据,说明所有item包含DragTarget组件
  3. 组件拖拽后原位置变为空白,设置Draggable的childWhenDragging=null即可
  4. 组件拖动过程中变大且为绿色带透明度,构建Draggable的feedback即可,修改颜色和透明度
  5. 组件拖动过程中,经过的组件后移,后移后原位置空出
    这点比较抽象,我们知道,每次掉用setState方法后将调用组件的build方法进行重绘,所以我们要修改的不是组件,而是数据
    移动过程中排序:则需调用onWillAccept的时候将Draggable数据插入DragTarget数据的位置
    移动经过的组件后移原位置变空白:首先我们知道,Draggable移动时,原位置已经变成了空白,其实我们可以在开始拖动时标记我们拖动的原Widget的数据,如果是正在拖动则返回一个空的Container即可,所以这时feedback经过的Target其实是自己
  6. 拖动结束后组件停留在当前位置
    onAccept中交换数据,清空标记

核心代码:

class _HJDraggableGridWidgetState extends State<HJDraggableGridWidget> {

  final _titles = ['OC', 'Swift', 'Java', 'C','C++','C#','Dart','Python','Go'];
  String _movingValue;// 记录正在移动的数据

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Draggable Demo')),
      body: Center(
        child: Container(
          width: 300,
          height: 300,
          color: Colors.grey,
          child: GridView(
            gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 3,
                mainAxisSpacing: 5.0,
                crossAxisSpacing: 5.0,
                childAspectRatio: 1),
            children: buildItems(),
            physics: NeverScrollableScrollPhysics(), //禁止滚动
          ),
        ),
      ),
    );
  }

  // 生成GridView的items
  List<Widget> buildItems() {
    List<Widget> items = List<Widget>();
    _titles.forEach((value) {
      items.add(draggableItem(value));
    });
    return items;
  }

  // 生成可拖动的item
  Widget draggableItem(value) {
    return Draggable(
      data: value,
      child: DragTarget(
        builder: (context, candidateData, rejectedData) {
          return baseItem(value,Colors.blue);
        },
        onWillAccept: (moveData) {
          print('=== onWillAccept: $moveData ==> $value');

          var accept = moveData != null;
          if (accept) {
            exchangeItem(moveData, value, false);
          }
          return accept;
        },
        onAccept: (moveData) {
          print('=== onAccept: $moveData ==> $value');
          exchangeItem(moveData, value, true);
        },
        onLeave: (moveData) {
          print('=== onLeave: $moveData ==> $value');
        },
      ),
      feedback: baseItem(value,Colors.green.withOpacity(0.8)),
      childWhenDragging: null,
      onDragStarted: () {
        print('=== onDragStarted');
        setState(() {
          _movingValue = value;//记录开始拖拽的数据
        });
      },
      onDraggableCanceled: (Velocity velocity, Offset offset) {
        print('=== onDraggableCanceled');
        setState(() {
          _movingValue = null;//清空标记进行重绘
        });
      },
      onDragCompleted: () {
        print('=== onDragCompleted');
      },
    );
  }
 // 基础展示的item 此处设置width,height对GridView 无效,主要是偷懒给feedback用
  Widget baseItem(value, bgColor) {
    if (value == _movingValue) {
      return Container();
    }
    return Container(
      width: 110,
      height: 110,
      color: bgColor,
      child: Center(
        child: Text(
          value,
          textAlign: TextAlign.center,
          style: TextStyle(
              fontWeight: FontWeight.bold,
              fontSize: 20,
              color: Colors.yellowAccent),
        ),
      ),
    );
  }

  // 重新排序
  void exchangeItem(moveData, toData, onAccept) {
    setState(() {
      var toIndex = _titles.indexOf(toData);

      _titles.remove(moveData);
      _titles.insert(toIndex, moveData);

      if (onAccept) {
        _movingValue = null;
      }
    });
  }
}


    若大家对上述内容有疑问,可以在下边发表评论,一起学习,共同进步。😁

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,826评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,968评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,234评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,562评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,611评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,482评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,271评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,166评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,608评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,814评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,926评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,644评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,249评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,866评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,991评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,063评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,871评论 2 354

推荐阅读更多精彩内容