美文网首页All in Flutter博客资源Flutter自定义View
Flutter初探--可拖拽排序的九宫格

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

作者: 我亦飘零久93 | 来源:发表于2019-06-15 21:17 被阅读9次

        本文通过可拖拽排序的九宫格对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;
          }
        });
      }
    }
    
    

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

    相关文章

      网友评论

        本文标题:Flutter初探--可拖拽排序的九宫格

        本文链接:https://www.haomeiwen.com/subject/lceifctx.html