Flutter自定义View之任务回传控制
作者:访客发布时间:2023-12-07分类:程序开发学习浏览:93
简介
Flutter作为UI框架,和其他语言一样支持自定义View。In this paper,we will introduce how to use Flutter to achieve a randomized suspension control.片尾附源码。
场景
作为一个程序员,听歌几乎是写代码的必备。一般的听力软件都会有这样一个效果,当我们听到时,打开另一个桌面时,正在播放歌曲的桌面以一个CD盘的形式悬浮在桌面上,可以随意点击CD盘又会重新回到当前播放的桌面上。
先看效果:
实现功能
This article mainly discusses how to use Flutter to achieve this effect.
需要实现的功能如下:
- 支持自定义初始位置。
- 随意拖动悬浮控件。
- 控件支持回弹效果。
- 控件不能滑出页面。
功能分析
初始位置
在Flutter定义控件的位置一般使用Positioned控件。通过指定left和top属性来初始化浮动按钮的位置。
- The most important part of this study is to explore the relationship between the mortality and the incidence of hyperlipidemia in patients with hyperlipidemia.
late Offset _offset;
- Then将初始值的值发送给Positioned Control.
Positioned(
left: _offset.dx,
top: _offset.dy,
....
)
这样就实现了初始化控件的位置。
随意拖拽
要想实现手势功能,在Flutter中就不得不使用GestureDetector 了,GestureDetector提供大量的手势操作回调,简化用户的使用。这里主要使用GestureDetector
onTap:点击,
onPanStart:滑动启动,
onPanUpdate:滑动更新,
onPanEnd:滑动结束
这几个方法。Through calculating the sliding position of the finger on the screen,then updating the control UI can be finished.
GestureDetector(
//滑动开始
onPanStart: (DragStartDetails details) {
_animationController.stop(canceled: true);
_selfWidth = context.size?.width ??0;
_selfHeight =context.size?.height ??0;
},
//滑动更新
onPanUpdate: (DragUpdateDetails details) {
_offset += Offset(details.delta.dx, details.delta.dy);
_offsetTop += details.delta.dy;
_offsetLeft += details.delta.dx;
if (_offsetTop < 0) _offsetTop = 0 + (widget.marginTop ?? 0.0);
if (_offsetLeft < 0) _offsetLeft = 0 + (widget.marginLeft ?? 0.0);
if (_offsetLeft + _selfWidth > _screenWidth) {
_offsetLeft = _screenWidth - _selfWidth;
}
if (_offsetTop + _selfHeight > _screenHeight) {
_offsetTop = _screenHeight - _selfHeight;
}
//重绘UI
setState(() {});
},
//滑动结束
onPanEnd: (DragEndDetails details) {
if (_offsetLeft > _screenWidth / 2 - _selfWidth / 2) {
_offsetLeft =
_screenWidth - _selfWidth - (widget.marginRight ?? 0.0);
} else {
_offsetLeft = 0.00 + (widget.marginLeft ?? 0.0);
}
回弹效果
处理滑翔动作,要增加回弹效果,就是要使用Flutter提供的动画。这里使用的是弹簧效果。
- 定义弹簧动画效果。
final double? mass; //弹簧质量
final double? stiffness; //弹簧系数
final double? damping; //阻尼系数
_animation = _animationController.drive(
Tween<Offset>(begin: _offset, end: Offset(_offsetLeft, _offsetTop)));
SpringSimulation simulation = SpringSimulation(
SpringDescription(mass: _mass, stiffness: _stiffness, damping: _damping),
0,
1,
-Offset(pixelsPerSecond.dx / size.width, pixelsPerSecond.dy / size.height)
.distance,
);
_animationController.animateWith(simulation);
可通过调节弹簧质量,弹簧系数,阻尼系数来控制得到不同的弹簧效果。
- In this paper,the author introduces the principle and application of the mechanism of mechanical mechanism.
_animationController = AnimationController.unbounded(vsync: this);
_animationController.addListener(() {
_offset = _animation.value;
setState(() {});
});
边界处理
最后我们来处理一下边界问题,如果不小心将控件滑出屏幕 ,那岂不是使用不了。所以当将控件滑出屏幕时,控件能够自动滑到屏幕两边。这里我们需要再手势滑动过程中,判断是否超出了屏幕边界。
onPanUpdate: (DragUpdateDetails details) {
_offset += Offset(details.delta.dx, details.delta.dy);
_offsetTop += details.delta.dy;
_offsetLeft += details.delta.dx;
if (_offsetTop < 0) _offsetTop = 0 + (widget.marginTop ?? 0.0);
if (_offsetLeft < 0) _offsetLeft = 0 + (widget.marginLeft ?? 0.0);
if (_offsetLeft + _selfWidth > _screenWidth) {
_offsetLeft = _screenWidth - _selfWidth;
}
if (_offsetTop + _selfHeight > _screenHeight) {
_offsetTop = _screenHeight - _selfHeight;
}
setState(() {});
},
通过实时监听手指在屏幕的偏移位置来限制控件的位置。
难点
计算屏幕高度:
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
final RenderBox parentRenderBox =
widget.parentKey.currentContext?.findRenderObject() as RenderBox;
_screenHeight = parentRenderBox.size.height - (widget.marginBottom ?? 0.0);
});
The height of the cylindrical portion 110 is determined based on the height of the cylindrical portion 110 and the cylindrical portion 112.
源码如下:
import 'package:flutter/material.dart';
import 'package:flutter/physics.dart';
class OverlayButton extends StatefulWidget {
const OverlayButton(
{Key? key,
required this.child,
required this.parentKey,
required this.initOffset,
required this.onPressed,
this.marginLeft,
this.marginTop,
this.marginBottom,
this.marginRight,
this.mass,
this.stiffness,
this.damping})
: super(key: key);
final Widget child; //子widget
final Offset initOffset; //初始位置
final GlobalKey parentKey; //父控件的key
final VoidCallback onPressed; //点击事件
final double? marginLeft; //外边距,距离左边
final double? marginTop; //外边距,距离上边
final double? marginRight; //外边距,距离右边
final double? marginBottom; //外边距,距离下边
final double? mass; //弹簧质量
final double? stiffness; //弹簧系数
final double? damping; //阻尼系数
@override
State createState() => _OverlayButtonState();
}
class _OverlayButtonState extends State<OverlayButton>
with SingleTickerProviderStateMixin {
late double _offsetLeft;
late double _offsetTop;
late AnimationController _animationController;
late Animation<Offset> _animation;
late Offset _offset;
late double _screenWidth;
late double _screenHeight;
late double _selfWidth = 0.0;
late double _selfHeight = 0.0;
late double _mass;
late double _stiffness;
late double _damping;
@override
void initState() {
super.initState();
_mass = widget.mass ?? 20;
_stiffness = widget.stiffness ?? 400;
_damping = widget.damping ?? 0.75;
_offsetLeft = widget.initOffset.dx;
_offsetTop = widget.initOffset.dy;
_offset = widget.initOffset;
_animationController = AnimationController.unbounded(vsync: this);
_animationController.addListener(() {
_offset = _animation.value;
setState(() {});
});
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
final RenderBox parentRenderBox =
widget.parentKey.currentContext?.findRenderObject() as RenderBox;
_screenHeight = parentRenderBox.size.height - (widget.marginBottom ?? 0.0);
});
}
@override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
_screenWidth = size.width;
return Positioned(
left: _offset.dx,
top: _offset.dy,
child: GestureDetector(
onPanStart: (DragStartDetails details) {
_animationController.stop(canceled: true);
_selfWidth = context.size?.width ??0;
_selfHeight =context.size?.height ??0;
},
onPanUpdate: (DragUpdateDetails details) {
_offset += Offset(details.delta.dx, details.delta.dy);
_offsetTop += details.delta.dy;
_offsetLeft += details.delta.dx;
if (_offsetTop < 0) _offsetTop = 0 + (widget.marginTop ?? 0.0);
if (_offsetLeft < 0) _offsetLeft = 0 + (widget.marginLeft ?? 0.0);
if (_offsetLeft + _selfWidth > _screenWidth) {
_offsetLeft = _screenWidth - _selfWidth;
}
if (_offsetTop + _selfHeight > _screenHeight) {
_offsetTop = _screenHeight - _selfHeight;
}
setState(() {});
},
onPanEnd: (DragEndDetails details) {
if (_offsetLeft > _screenWidth / 2 - _selfWidth / 2) {
_offsetLeft =
_screenWidth - _selfWidth - (widget.marginRight ?? 0.0);
} else {
_offsetLeft = 0.00 + (widget.marginLeft ?? 0.0);
}
startAnimation(details.velocity.pixelsPerSecond, size);
},
onTap: widget.onPressed,
child: widget.child,
),
);
}
void startAnimation(Offset pixelsPerSecond, Size size) {
_animation = _animationController.drive(
Tween<Offset>(begin: _offset, end: Offset(_offsetLeft, _offsetTop)));
SpringSimulation simulation = SpringSimulation(
SpringDescription(mass: _mass, stiffness: _stiffness, damping: _damping),
0,
1,
-Offset(pixelsPerSecond.dx / size.width, pixelsPerSecond.dy / size.height)
.distance,
);
_animationController.animateWith(simulation);
}
@override
void dispose() {
super.dispose();
_animationController.dispose();
}
}
本文简单实现了一个拖拽回弹的控件,更多好玩的效果需要动手去实现。
- 程序开发学习排行
-
- 1鸿蒙HarmonyOS:Web组件网页白屏检测
- 2HTTPS协议是安全传输,为啥还要再加密?
- 3HarmonyOS鸿蒙应用开发——数据持久化Preferences
- 4记解决MaterialButton背景颜色与设置值不同
- 5鸿蒙HarmonyOS实战-ArkUI组件(RelativeContainer)
- 6鸿蒙HarmonyOS实战-ArkUI组件(Stack)
- 7[Android][NDK][Cmake]一文搞懂Android项目中的Cmake
- 8Android广播如何解决Sending non-protected broadcast问题
- 9鸿蒙HarmonyOS实战-ArkUI组件(mediaquery)
- 最近发表
-
- WooCommerce最好的WordPress常用插件下载博客插件模块的相关产品
- 羊驼机器人最好的WordPress常用插件下载博客插件模块
- IP信息记录器最好的WordPress常用插件下载博客插件模块
- Linkly for WooCommerce最好的WordPress常用插件下载博客插件模块
- 元素聚合器Forms最好的WordPress常用插件下载博客插件模块
- Promaker Chat 最好的WordPress通用插件下载 博客插件模块
- 自动更新发布日期最好的WordPress常用插件下载博客插件模块
- WordPress官方最好的获取回复WordPress常用插件下载博客插件模块
- Img to rss最好的wordpress常用插件下载博客插件模块
- WPMozo为Elementor最好的WordPress常用插件下载博客插件模块添加精简版