原文

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...