Android学习记录Flutter实践经验记录

Flutter 可拖拽Widget

2021-01-08  本文已影响0人  旺仔_100

一、背景
我们经常会看到管理手机软件那种悬浮小圆圈,或者微信公众号的悬浮按钮。它们不仅能悬浮在所有页面之上,还可以在界面任意拖动。我们使用flutter 改如何实现呢?

二、思路分析
1.全局弹窗。这个在flutter里面有一个 Overlay.of(context).insert(overlayEntry);
这个就是可以全局浮动的弹窗。
2.任意拖动。刚好flutter有个Draggable控件,它可以直接拖动一个widget。但是它一松手就会回到之前的位置。
3.为了Draggable控件停留在我们想要的位置,那么久引入了DragTarget。

三、每一个Widget介绍
1.Overlay:Overlay 之于 Flutter , 有点相当于 KeyWindow 之于 iOS 一样,可以将子 widget 置于其他 widget 的顶层,带来 “悬浮”的效果。
2.OverlayEntry:OverlayEntry 之于 Overlay,对于 iOS 开发而言,又有点 subView 之于 KeyWindow 的味道了。 OverlayEntry 是视图的实际的容器, 把其往 Overlay 那儿添加了,就可以成像了。
3.Draggable

const Draggable({
 Key key,
 @required this.child,              // 初始化显示的 widget
 @required this.feedback,       // 拖拽过程中(活动中)显示的 widget
 this.data,                                 // widget 携带的数据,放手时可以将这个 data 数据传递出去
 this.axis,                                 // 限制 draggable 的移动范围
 this.childWhenDragging,            // 拖住动作发生过程中,初始化位置显示的 widget
 this.feedbackOffset = Offset.zero, // 当 feedback 与 child 相比,有 transform 的时候,需要用到这个属性来调整 hittest 范围
 this.dragAnchor = DragAnchor.child, //锚点
 this.affinity,                         // 单词的意思是亲和力,当 Draggable 位于 另外一个 Scrollable 控件內时,来控制到底这个这个拖拽事件到底由 Draggable 响应,还是由 Scrollable 控件来响应
 this.maxSimultaneousDrags, // 限制有多少个 Draggable 同时发生 拖拽动作
 this.onDragStarted,                    // 拖拽动作开始回调
 this.onDraggableCanceled,      // 拖拽动作取消回调
 this.onDragEnd,                            //拖拽动作结束回调
 this.onDragCompleted,              // 拖拽动作完成回调, 并被一个 DragTarget 接收
 this.ignoringFeedbackSemantics = true, // 也是看了文档才知道,这个属性还是有点用的,当 feedback 跟 child 是同一个 widget A 对象时,就应该把这个属性设成 false, 配合赋值一个 GlobalKey,这样,这个 widget A 就不会在 feedback 跟 child 切换时,重新销毁后又创建了。这个在 widget A 带有播放动画是比较容易看出区别,每次手指拖放都伴随着动画的重新开始
})

4.DragTarget

const DragTarget({
  Key key,
  @required this.builder,  //根据 Draggable 传过来的 data ,来显示想要的 widget
  this.onWillAccept,            // 根据传过来的 data ,选择是否接收这个 Draggable, 返回 true 则激活 onAccept
  this.onAccept,                    // Draggable 被丢进了这个 DragTarget 区域后回调
  this.onLeave,                     // Draggable 离开 DragTarget 区域后的回调
}) : super(key: key);

四、完整代码

import 'package:flutter/cupertino.dart';

class DragOverlay {
  static Widget view;
  static OverlayEntry _holder;

  static void remove() {
    if (_holder != null) {
      _holder.remove();
      _holder = null;
    }
  }

  static void show({@required BuildContext context, @required Widget view}) {
    DragOverlay.view = view;
    remove();
    OverlayEntry overlayEntry = OverlayEntry(builder: (context){
      return Positioned(
        top: MediaQuery.of(context).size.height *0.7,
        child: _buildDraggable(context),
      );
    });
    Overlay.of(context).insert(overlayEntry);
    _holder = overlayEntry;
  }

  static _buildDraggable(context){
    return Draggable(
      child: view,
      feedback: view,
      onDragStarted: (){

      },
      onDragEnd: (detail){
        print("onDraEnd:${detail.offset}");
        //放手时候创建一个DragTarget
        createDragTarget(offset:detail.offset,context:context);
      },
      //当拖拽的时候就展示空
      childWhenDragging: Container(),
      ignoringFeedbackSemantics: false,
    );
  }

  static void createDragTarget({Offset offset,BuildContext context}){
     if(_holder != null){
       _holder.remove();
     }
     _holder = new OverlayEntry(builder: (context){
       bool isLeft = true;
       if(offset.dx + 100 > MediaQuery.of(context).size.width / 2){
         isLeft = false;
       }
       double maxY = MediaQuery.of(context).size.height - 100;

       return Positioned(
         top: offset.dy < 50 ? 50 : offset.dy > maxY ? maxY : offset.dy,
         left: isLeft ? 0:null,
         right: isLeft ? null : 0,
         child: DragTarget(
           onWillAccept: (data){
             print('onWillAccept:$data');
             ///返回true 会将data数据添加到candidateData列表中,false时会将data添加到rejectData
             return true;
           },
           onAccept: (data){
             print('onAccept : $data');
           },
           onLeave: (data){
             print("onLeave");
           },
           builder: (BuildContext context,List incoming,List rejected){
             return _buildDraggable(context);
           },
         ),
       );
     });
     Overlay.of(context).insert(_holder);
  }
}

五、调用

DragOverlay.show(context: context, view: Container(
      width: 100,
      height: 20,
      color: Colors.red,
    ));
上一篇下一篇

猜你喜欢

热点阅读