original
https://medium.com/flutterdevs/draggable-floating-action-button-in-flutter-2149a7e47f06
refer to
- https://pub.flutter-io.cn/packages/get#reactive-state-manager
- https://dart.dev/guides/language/extension-methods
text
Flutter allows you to add a floating action button using the FloatingActionButton widget. Nevertheless, it does not allow you to drag the button. Consider the possibility that you need to make it draggable. This article has a model that exposes what needs to be done to make a floating action button. This button can be dragged anywhere on the screen as long as it is in the parent widget.
In this blog, we will explore Flutter's drag floating button. We will see how to implement a drag-and-drop action button for a demo program and show how to create your Flutter application.
Introduction
The following demo video shows how to create a draggable floating action button in Flutter. It shows how dragging the floating action button will work in your Flutter application. It shows that when the code runs successfully, the user drags a floating action button anywhere around the screen, as long as it is in the parent widget. It will be displayed on your device.
How to implement the code in the dart file
You need to implement it in your code separately:
Create a new dart file named main.dart in the lib folder.
First, we will create a Globalkey
and name it _parentKey
final GlobalKey _parentKey = GlobalKey();
In the body, we will add a Container widget with height and width. It is a sub-attribute and we will add the Stack widget. In this widget, we will add a key, text, and a DraggableFloatingActionButton (). Inside the button, we will add a container with height and width. Add images in its sub-attributes. In addition, we will add initialOffset, parent key, and onPressed. We will define the following code in depth.
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: () {},
),
],
),
)
Create a code file draggable_floating_action_button.dart
We will create a class for such widgets. The main issue we need to deal with is the ability to make the button draggable behind the pointer. One of the widgets that can be used is a listener, which can recognize pointer movement events and give movement details. Basically, the button should be wrapped as a child of the listener.
The Listener widget has onPointerMove contention, which can be used to pass a callback that will be considered when the pointer moves. The callback function should have a boundary PointerMoveEvent
, which contains the development increment (increment) in the x and y titles. Dx and delta. ). The offset of the catch should be refreshed by moving the delta.
The following is a class for making draggable floating operation buttons. It has several arguments, including sub, initialOffset, and onPressed. The child widgets are delivered using positioning widgets that depend on the current offset. In addition, it is also packaged as a sub-component of the listener widget. In addition, there is a strategy _updatePosition
to refresh the current offset according to the increment of movement.
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,
),
);
}
}
You need to add a key to the parent widget and pass it to the DraggableFloatingActionButton widget. You can get the RenderBox from the currentContext property, which has a findRenderObject strategy. Then, at this point, you can get the size of the parent from the size property of the RenderBox. You should be cautious because the findRenderObject technique should be called after building the tree. Later, you need to use the addPostFrameCallback of WidgetsBinding to call it.
The technology of _updatePosition
should also be changed. If the new offset is less than the minimum offset, the value must be set to the minimum offset. If the new offset is more noteworthy than the maximum offset, the value must be set to the maximum offset. This is required for both the x-axis and y-axis.
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,
),
),
);
}
}
When we run the application, we should get the output of the screen, just like the screenshot below.
All codes
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: () {},
),
],
),
)
],
),
);
}
}
Concluding remarks
In this article, I have explained the basic structure of the draggable floating button, you can modify this code according to your choice. This is a small introduction to draggable floating operation buttons for user interaction. From my side, it works using Flutter.
I hope this blog will provide you with sufficient information, try to drag the floating action button in your Flutter project. We will show you what the introduction is? This is how to make a draggable floating button in Flutter. Fundamentally, you can use the Listener widget to distinguish pointer movement events and update the button offset based on the development increment. The listener widget also supports distinguishing pointer events. Unless the button has been dragged recently, the button's activity should be performed on these events. Similarly, you need to obtain the parent button and the size of the button to prevent the button from exceeding the scope of the parent button. So please try it.
© Cat brother
- https://ducafecat.tech/
- https://github.com/ducafecat
- WeChat group ducafecat
- b Station https://space.bilibili.com/404904528
Past
Open source
GetX Quick Start
https://github.com/ducafecat/getx_quick_start
News client
https://github.com/ducafecat/flutter_learn_news
strapi manual translation
WeChat discussion group ducafecat
Series collection
Translation
https://ducafecat.tech/categories/%E8%AF%91%E6%96%87/
Open source project
https://ducafecat.tech/categories/%E5%BC%80%E6%BA%90/
Dart programming language basics
https://space.bilibili.com/404904528/channel/detail?cid=111585
Getting started with Flutter zero foundation
https://space.bilibili.com/404904528/channel/detail?cid=123470
Flutter actual combat from scratch news client
https://space.bilibili.com/404904528/channel/detail?cid=106755
Flutter component development
https://space.bilibili.com/404904528/channel/detail?cid=144262
Flutter Bloc
https://space.bilibili.com/404904528/channel/detail?cid=177519
Flutter Getx4
https://space.bilibili.com/404904528/channel/detail?cid=177514
Docker Yapi
https://space.bilibili.com/404904528/channel/detail?cid=130578
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。