原文
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…