likes
comments
collection
share

Flutter 系列 - 拖动元素本教程,我们将从零开始,实现一个拖动元素的功能,并限制元素拖动活动的区间。 开始项目

作者站长头像
站长
· 阅读数 47

本教程,我们将从零开始,实现一个拖动元素的功能,并限制元素拖动活动的区间。

开始项目

$ flutter create drag_demo
$ cd drag_demo
$ flutter run

Flutter 系列 - 拖动元素本教程,我们将从零开始,实现一个拖动元素的功能,并限制元素拖动活动的区间。 开始项目

启动项目之后,我们选择在 macOS 上运行桌面端应用,初始效果如下👇

Flutter 系列 - 拖动元素本教程,我们将从零开始,实现一个拖动元素的功能,并限制元素拖动活动的区间。 开始项目

实现拖动

我们将会修改 main.dart 文件,实现拖动的效果。

我们先来实现基本的 UI 👇

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: [
          Positioned(
            left: MediaQuery.of(context).size.width / 2 - 50.0,
            top: MediaQuery.of(context).size.height / 2 - 50.0,
            child: Container(
              width: 100.0,
              height: 100.0,
              color: Colors.red,
            ),
          ),
        ],
      ),
    );
  }
}

这里很简单,我们在窗口上新建一个简单的 Container 的挂件,将其设定在屏幕的中央。MediaQuery.of(context).size.width 即是屏幕的宽度。

Flutter 系列 - 拖动元素本教程,我们将从零开始,实现一个拖动元素的功能,并限制元素拖动活动的区间。 开始项目

认识 Draggable

我们将会使用 Draggable,官网的实现效果如下👇

Flutter 系列 - 拖动元素本教程,我们将从零开始,实现一个拖动元素的功能,并限制元素拖动活动的区间。 开始项目

嗯,效果不是我们需要的。那么,我们先来看看 Draggable 这个挂件能干啥:

const Draggable({
  super.key,
  required this.child, // 拖动的子挂件,必须
  required this.feedback, // 当拖动正在进行时,在指针下方显示的窗口小部件,必须
  this.data, // 该挂件带的数据(传递给其他挂件,比如上面案例 data: 10, 每次拖动增加一个 10)
  this.axis,
  this.childWhenDragging, // 当拖动的时候(在原位置)展示的挂件
  this.feedbackOffset, 
  this.dragAnchorStrategy = childDragAnchorStrategy,
  this.affinity,
  this.maxSimultaneousDrags,
  this.onDragStarted, // 挂件拖动开始
  this.onDragUpdate,
  this.onDraggableCanceled,
  this.onDragEnd, // 关键拖动结束
  this.onDragCompleted,
  this.ignoringFeedbackSemantics = true,
  this.ignoringFeedbackPointer = true,
  this.rootOverlay = false,
  this.hitTestBehavior = HitTestBehavior.deferToChild,
  this.allowedButtonsFilter,
})

上面是 Draggble 挂件的所有属性和方法,我们在重点的那几个上标注了说明,我们也是对这几个用得多,其他的说明,感兴趣的读者可以查看 Draggable class

下面,我们针对这 feedback, childWhenDragging, onDragStartedonDragEnd 来编写一个 Demo,方便理解,我们在 _MyHomePageState 类上进行改写:

class _MyHomePageState extends State<MyHomePage> {
  Offset position = const Offset(0.0, 0.0);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: [
          Positioned(
            left: position.dx,
            top: position.dy,
            child: Draggable(
              feedback: Container(
                width: 100.0,
                height: 100.0,
                color: Colors.black,
                child: const Center(
                  child: Text(
                    'Dragging...',
                    style: TextStyle(
                      fontSize: 16.0,
                      color: Colors.white,
                    ),
                  ),
                ),
              ),
              childWhenDragging: Container(
                width: 100.0,
                height: 100.0,
                color: Colors.green,
                child: const Center(
                  child: Text('Child When Dragging'),
                ),
              ),
              onDragStarted: () {
                print('drag start');
              },
              onDragEnd: (dragDetails) {
                setState(() {
                  position =
                  Offset(dragDetails.offset.dx, dragDetails.offset.dy);
                });
              },
              child: Container(
                width: 100.0,
                height: 100.0,
                color: Colors.red,
              ),
            ),
          ),
        ],
      )
    );
  }
}

上面,我们设定了一个偏移的位置 position,并赋予其初始值 Offset(0.0, 0.0),然后为 feedback 设置一个 Container 的拖动反馈提示;为 childWhenDragging 设置一个正在拖动的替换 Container 挂件;当我们 onDragStarted 开始拖动的时候需要做什么,这么就简单打印个文案 drag start;当我们拖动完成的时候,更改该挂件的偏移位置,结合下面的 GIF 图更好理解👇

Flutter 系列 - 拖动元素本教程,我们将从零开始,实现一个拖动元素的功能,并限制元素拖动活动的区间。 开始项目

应用 Draggable

认识完 Draggable 之后,我们实现拖动的需求。我们还是改写 _MyHomePageState 类:

class _MyHomePageState extends State<MyHomePage> {
  Offset position = const Offset(0.0, 0.0);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: [
          Positioned(
            left: position.dx,
            top: position.dy,
            child: Draggable(
              feedback: _buildDragWidget(),
              childWhenDragging: Container(),
              onDragEnd: (dragDetails) {
                setState(() {
                  position =
                  Offset(dragDetails.offset.dx, dragDetails.offset.dy);
                });
              },
              child: _buildDragWidget(),
            ),
          ),
        ],
      )
    );
  }

  Widget _buildDragWidget() {
    return Container(
      width: 100.0,
      height: 100.0,
      color: Colors.red,
    );
  }
}

上面 feebackchild 我们应该使用同一个 Widget,所以,我们这里共同调用了 _buildGragWidget 方法。

Flutter 系列 - 拖动元素本教程,我们将从零开始,实现一个拖动元素的功能,并限制元素拖动活动的区间。 开始项目

我们能够很顺畅地拖动挂件了,但是,当我们拖动出去视窗的时候,回不来了,尴尬😅。

下面,我们在方法 onDragEnd 方法上做下限制:

onDragEnd: (dragDetails) {
  double _dx = dragDetails.offset.dx;
  if(dragDetails.offset.dx < 0.0) {
    _dx = 0.0;
  }
  if(dragDetails.offset.dx >= MediaQuery.of(context).size.width - 100.0) {
    _dx = MediaQuery.of(context).size.width - 100.0;
  }

  double _dy =dragDetails.offset.dy;
  if(dragDetails.offset.dy < 0.0) {
    _dy = 0.0;
  }
  if(dragDetails.offset.dy >= MediaQuery.of(context).size.height - 100.0) {
    _dy = MediaQuery.of(context).size.height - 100.0;
  }

  setState(() {
    position =
    Offset(_dx, _dy);
  });
},

我们限制了拖动的挂件只能够在视窗上进行展示👇

Flutter 系列 - 拖动元素本教程,我们将从零开始,实现一个拖动元素的功能,并限制元素拖动活动的区间。 开始项目

我们留个问题:如何使用 Draggable 实现拖动指定的区域,带动整个父 Widget 拖动呢?

答案 - 点击展开
使用 onDragUpdate 方法(给出参考实现如下 👇)
                                /// main.dart - 更改其类 _MyHomePageState
class _MyHomePageState extends State {
  Offset position = const Offset(0.0, 0.0);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: [
          Positioned(
            left: position.dx,
            top: position.dy,
            child: Stack(
              children: [
                Container(
                  width: 100.0,
                  height: 100.0,
                  color: Colors.red,
                ),
                Positioned(
                  top: 0.0,
                  right: 0.0,
                  child: Draggable(
                    feedback: _buildDragWidget(),
                    childWhenDragging: Container(),
                    onDragUpdate: (details) {
                      double _dx = position.dx + details.delta.dx;
                      double _dy = position.dy + details.delta.dy;
                      setState(() {
                        position = Offset(_dx, _dy);
                      });
                    },
                    child: _buildDragWidget(),
                  ),
                )
              ],
            ),
          )
        ],
      ),
    );
  }
  Widget _buildDragWidget() {
    return Container(
      width: 30.0,
      height: 30.0,
      color: Colors.blue,
      child: const Center(
        child: Icon(
          Icons.drag_indicator,
          color: Colors.white
        ),
      ),
    );
  }
}
                
            

            

【完✅】感谢捧场🌹

转载自:https://juejin.cn/post/7410624782418935862
评论
请登录