Flutter GridView 拖拽图标列表实现插入删除动画效果(一)

452 阅读2分钟

GridView 拖拽图标列表实现插入删除动画效果(一)

平移动画

Flutter 提供了四个Widget可实现平移动画,分别是

  • SlideTransition
  • AlignTransition
  • PositionedTransition
  • ReleativePositionedTransition

我们采用第一种 SlideTransition SlideTransition基于 Animation 来确定平移位置。平移的具体距离,由Widget自身的宽高 * Offset中的 x,y 值

AnimationController _slideController = 
        AnimationController(duration: const Duration(milliseconds: 300), vsync: this);
Animation<Offset> animate = 
        Tween(begin: const Offset(0, 0), end: const Offset(1, 1)).animate(_slideController);
return SlideTransition(position: animate, child: child);

这样就能实现目标Widget 从原点移动到右下角自身宽高的距离,学会使用平移动画之后,只要在拖拽目标移动时,判断出每一个Widget需要移动到什么位置,就能实现插入删除效果了

Flutter 拖拽效果

拖拽组件

class Draggable<T extends Object> extends StatefulWidget{}

Draggable<Item>(
    onDragStarted: () {
      // 开始拖拽 删除拖拽的item 然后开始删除动画
    },
    onDraggableCanceled: (v, o) {
          
    },
    data: item,
    feedback: ,
    dragAnchorStrategy: pointerDragAnchorStrategy,
    child: child,
),

接收组件

class DragTarget<T extends Object> extends StatefulWidget {}

DragTarget<Item>(
    onAccept: (item) {
        // 降拖拽组件插入到当前位置,刷新        
    }
    onWillAccept: (item) {
        // 开始插入动画
    },
    builder: (context, candidateItems, rejectedItems) {
        return child;
    },
);

核心算法

我们把每一个GirdView中的item项都看做是一个DragTarget,然后在将要接受拖拽组件时,计算出每一个widget将移动的offset 例 : list = [1,2,3,4,5,6] 我们正在拖拽的item为7 假如gridview 是一个3列的列表 那么 list当前的排列是这样的

image.png

当我们把7放到3上,那么新的排列应该是什么样的?

image.png

list = [1,2,3,4,5,6]
remainsList = [1,2,7,3,4,5,6]
animate = createTargetItemSlideAnimation(2)

Animation<Offset>? createTargetItemSlideAnimation(int index) {
    int? targetIndex;
    if (remainsList.contains(list[index])) {
      targetIndex = remainsList.indexOf(list[index]);
    }
    if (targetIndex != null && index != targetIndex) {
      return Tween(begin: const Offset(0, 0), end: getTargetOffset(index, targetIndex)).animate(_slideController);
    } else {
      return null;
    }
}

Offset getTargetOffset(int startIndex, int endIndex) {
    int horizontalCount = 3;
    int horizontalIndex = endIndex % horizontalCount - startIndex % horizontalCount;
    int verticalIndex = endIndex ~/ horizontalCount - startIndex ~/ horizontalCount;
    double dx = horizontalIndex.toDouble();
    double dy = verticalIndex.toDouble();
    return Offset(dx, dy);
}

这样就能获取每一个widget应该移动的offset了 接着调用_slideController.forward().whenComplete((){})就可以开始动画并在动画结束后做出相应的逻辑了

下一期给出源码