乐趣区

关于flutter:Flutter-可拖动的悬浮动作按钮

原文

https://medium.com/flutterdev…

参考

  • https://pub.flutter-io.cn/pac…
  • https://dart.dev/guides/langu…

注释

Flutter 容许您应用 FloatingActionButton 小部件增加一个浮动操作按钮。尽管如此,它不容许你拖动按钮。考虑一下你须要让它可拖动的可能性。本文有一个模型,它公开了制作一个浮动操作按钮须要做的事件,这个按钮能够在屏幕的任何中央拖动,只有它在父窗口小部件中。

在这个博客中,咱们将摸索 Flutter 的拖动浮动按钮。咱们将看到如何实现一个演示程序的拖曳浮动口头按钮,并显示如何创立您的 Flutter 应用程序。

简介

上面的演示视频显示了如何创立一个可拖动的沉没动作按钮在 Flutter。它显示了如何拖动浮动口头按钮将在您的 Flutter 应用程序的工作。它显示当代码胜利运行时,用户将一个浮动操作按钮拖动到屏幕四周的任何中央,只有它在父窗口小部件中。它会显示在你的设施上。

如何实现 dart 文件中的代码

你须要别离在你的代码中实现它:

在 lib 文件夹中创立一个名为 main.dart 的新 dart 文件。

首先,咱们将创立一个 Globalkey,并将其命名为 _parentKey

final GlobalKey _parentKey = GlobalKey();

在注释中,咱们将增加一个具备高度和宽度的 Container 小部件。它是子属性,咱们将增加 Stack 小部件。在这个小部件中,咱们将增加一个键、文本和一个 DraggableFloatingActionButton ()。在按钮外部,咱们将增加一个具备高度和宽度的容器。在其子属性中增加图像。此外,咱们还将增加 initialOffset、父键和 onPressed。咱们将深刻定义上面的代码。

Container(
  width: 300,
  height: 300,
  child: Stack(
    key: _parentKey,
    children: [Container(color: Colors.cyan),
      Center(
        child: const Text(
          "FlutterDev's.com",
          style: const TextStyle(color: Colors.white, fontSize: 24),
        ),
      ),
      DraggableFloatingActionButton(
        child: Container(
          width: 60,
          height: 60,
          decoration: ShapeDecoration(shape: CircleBorder(),
            color: Colors.white,
          ),
          child: Image.asset("assets/logo.png"),
        ),
        initialOffset: const Offset(120, 70),
        parentKey: _parentKey,
        onPressed: () {},
      ),
    ],
  ),
)

创立一个名为 draggable_floating_action_button.dart 的代码文件。

咱们将为这样的小部件创立一个类。咱们须要解决的次要问题是使按钮在指针之后可拖动的能力。能够应用的小部件之一是监听器,它能够辨认指针挪动事件并给出挪动细节。基本上,按钮应该作为侦听器的子级进行包装。

Listener 小部件具备 onPointerMove 争用,能够利用该争用来传递一个回调,该回调在指针挪动时将被思考。回调函数应该有一个边界 PointerMoveEvent,其中蕴含 x 和 y 题目中的开发增量 (增量)。Dx 和 delta。). 捕捉物的偏移量应该通过挪动三角洲来刷新。

上面是制作可拖动浮动操作按钮的类。它有几个争执,包含子,initialOffset,和 onPressed。子小部件是利用依赖于以后偏移量的定位小部件交付的。此外,它还被包装为监听器小部件的子部件。此外,还有一个策略 _updatePosition,依据挪动的增量刷新以后的偏移量。

class DraggableFloatingActionButton extends StatefulWidget {

  final Widget child;
  final Offset initialOffset;
  final VoidCallback onPressed;

  DraggableFloatingActionButton({
    required this.child,
    required this.initialOffset,
    required this.onPressed,
  });

  @override
  State<StatefulWidget> createState() => _DraggableFloatingActionButtonState();
}

class _DraggableFloatingActionButtonState extends State<DraggableFloatingActionButton> {

  bool _isDragging = false;
  late Offset _offset;

  @override
  void initState() {super.initState();
    _offset = widget.initialOffset;
  }

  void _updatePosition(PointerMoveEvent pointerMoveEvent) {
    double newOffsetX = _offset.dx + pointerMoveEvent.delta.dx;
    double newOffsetY = _offset.dy + pointerMoveEvent.delta.dy;

    setState(() {_offset = Offset(newOffsetX, newOffsetY);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Positioned(
      left: _offset.dx,
      top: _offset.dy,
      child: Listener(onPointerMove: (PointerMoveEvent pointerMoveEvent) {_updatePosition(pointerMoveEvent);

          setState(() {_isDragging = true;});
        },
        onPointerUp: (PointerUpEvent pointerUpEvent) {print('onPointerUp');

          if (_isDragging) {setState(() {_isDragging = false;});
          } else {widget.onPressed();
          }
        },
        child: widget.child,
      ),
    );
  }
}

您须要向父部件增加一个键,并将其传递给 DraggableFloatingActionButton 部件。您能够从 currentContext 属性中取得 RenderBox,该属性具备 findRenderObject 策略。而后,在这一点上,您能够从 RenderBox 的 size 属性取得父级的大小。您应该审慎,因为应该在构建树之后调用 findRenderObject 技术。随后,您须要利用 WidgetsBinding 的 addPostFrameCallback 来调用它。

技术的 _updatePosition 也应该扭转。如果新的偏移量小于最小偏移量,则该值必须设置为最小偏移量。如果新的偏移量比最大偏移量更值得注意,则该值必须设置为最大偏移量。对于 x 轴和 y 轴都须要这样做。

class DraggableFloatingActionButton extends StatefulWidget {

  final Widget child;
  final Offset initialOffset;
  final VoidCallback onPressed;

  DraggableFloatingActionButton({
    required this.child,
    required this.initialOffset,
    required this.onPressed,
  });

  @override
  State<StatefulWidget> createState() => _DraggableFloatingActionButtonState();
}

class _DraggableFloatingActionButtonState extends State<DraggableFloatingActionButton> {final GlobalKey _key = GlobalKey();

  bool _isDragging = false;
  late Offset _offset;
  late Offset _minOffset;
  late Offset _maxOffset;

  @override
  void initState() {super.initState();
    _offset = widget.initialOffset;

    WidgetsBinding.instance?.addPostFrameCallback(_setBoundary);
  }

  void _setBoundary(_) {final RenderBox parentRenderBox = widget.parentKey.currentContext?.findRenderObject() as RenderBox;
    final RenderBox renderBox = _key.currentContext?.findRenderObject() as RenderBox;

    try {
      final Size parentSize = parentRenderBox.size;
      final Size size = renderBox.size;

      setState(() {_minOffset = const Offset(0, 0);
        _maxOffset = Offset(
            parentSize.width - size.width,
            parentSize.height - size.height
        );
      });
    } catch (e) {print('catch: $e');
    }
  }

  void _updatePosition(PointerMoveEvent pointerMoveEvent) {
    double newOffsetX = _offset.dx + pointerMoveEvent.delta.dx;
    double newOffsetY = _offset.dy + pointerMoveEvent.delta.dy;

    if (newOffsetX < _minOffset.dx) {newOffsetX = _minOffset.dx;} else if (newOffsetX > _maxOffset.dx) {newOffsetX = _maxOffset.dx;}

    if (newOffsetY < _minOffset.dy) {newOffsetY = _minOffset.dy;} else if (newOffsetY > _maxOffset.dy) {newOffsetY = _maxOffset.dy;}

    setState(() {_offset = Offset(newOffsetX, newOffsetY);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Positioned(
      left: _offset.dx,
      top: _offset.dy,
      child: Listener(onPointerMove: (PointerMoveEvent pointerMoveEvent) {_updatePosition(pointerMoveEvent);

          setState(() {_isDragging = true;});
        },
        onPointerUp: (PointerUpEvent pointerUpEvent) {print('onPointerUp');

          if (_isDragging) {setState(() {_isDragging = false;});
          } else {widget.onPressed();
          }
        },
        child: Container(
          key: _key,
          child: widget.child,
        ),
      ),
    );
  }
}

当咱们运行应用程序时,咱们应该失去屏幕的输入,就像上面的屏幕截图一样。

全副代码

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_draggable_floating/draggable_floating_action_button.dart';
import 'package:flutter_draggable_floating/splash_screen.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Splash(),);
  }
}

class DraggableFloatingActionButtonDemo extends StatelessWidget {final GlobalKey _parentKey = GlobalKey();

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: AppBar(
        automaticallyImplyLeading: false,
        title: const Text('Flutter Draggable Floating Action Button'),
        backgroundColor: Colors.cyan,
      ),
      body: Column(
        children: [
          Container(height: 150,),
          Container(
            width: 300,
            height: 300,
            child: Stack(
              key: _parentKey,
              children: [Container(color: Colors.cyan),
                Center(
                  child: const Text(
                    "FlutterDev's.com",
                    style: const TextStyle(color: Colors.white, fontSize: 24),
                  ),
                ),
                DraggableFloatingActionButton(
                  child: Container(
                    width: 60,
                    height: 60,
                    decoration: ShapeDecoration(shape: CircleBorder(),
                      color: Colors.white,
                    ),
                    child: Image.asset("assets/logo.png"),
                  ),
                  initialOffset: const Offset(120, 70),
                  parentKey: _parentKey,
                  onPressed: () {},
                ),
              ],
            ),
          )
        ],
      ),
    );
  }
}

结语

在这篇文章中,我曾经解释了可拖动浮动按钮的根本构造,您能够依据本人的抉择批改这个代码。这是一个小的介绍可拖动浮动操作按钮对用户交互从我这边,它的工作应用 Flutter。

我心愿这个博客将提供给您充沛的信息,尝试在您的 Flutter 我的项目的拖曳浮动口头按钮。咱们将向您展现介绍是什么?. 这就是在 Flutter 制作一个可拖动的浮动按钮的办法。从根本上讲,您能够利用 Listener 小部件来辨别指针挪动事件,并依据开发增量更新按钮偏移量。监听器小部件同样反对辨别指针事件,除非按钮最近被拖动,否则应该在这些事件上执行按钮的流动。同样,您须要取得父按钮和按钮的大小,以避免按钮超出父按钮的范畴。所以请尝试一下。


© 猫哥

  • https://ducafecat.tech/
  • https://github.com/ducafecat
  • 微信群 ducafecat
  • b 站 https://space.bilibili.com/40…

往期

开源

GetX Quick Start

https://github.com/ducafecat/…

新闻客户端

https://github.com/ducafecat/…

strapi 手册译文

https://getstrapi.cn

微信探讨群 ducafecat

系列汇合

译文

https://ducafecat.tech/catego…

开源我的项目

https://ducafecat.tech/catego…

Dart 编程语言根底

https://space.bilibili.com/40…

Flutter 零根底入门

https://space.bilibili.com/40…

Flutter 实战从零开始 新闻客户端

https://space.bilibili.com/40…

Flutter 组件开发

https://space.bilibili.com/40…

Flutter Bloc

https://space.bilibili.com/40…

Flutter Getx4

https://space.bilibili.com/40…

Docker Yapi

https://space.bilibili.com/40…

退出移动版