Flutter 中的 Icon 按钮是一个静态按钮,虽然可以通过改变其颜色和透明度实现简单的动画,但显示效果和表现力在有些场合仍不能满足使用要求。
今天给大家提供一种在原 Icon 按钮外围带运行指示器的土星环按钮,按钮外围有两个近半圆的圆环组成,围绕原 Icon 按钮旋转,可以很直观的指示某项工作当前正在运行。
效果图因动画图制作要麻烦一些,上传要求也比较高,在此就不提供了,请大家谅解,安卓手机可以安装蘑菇仪表查看(点击主页右上角第二个按钮)。下面直接上代码。
SaturnButton.dart
import 'dart:math';
import 'package:flutter/material.dart';
// 绘制需动画的土星环图形,这里是上下对称接近半圆的两个圆弧
class SaturnRingPainter extends CustomPainter {
final ringColor;
var ringWidth = 3.0;
double isoscelesTriangle = 5.0;
SaturnRingPainter({Key? key, this.ringColor = Colors.green});
@override
void paint(Canvas canvas, Size size) {
var paint = Paint()
..isAntiAlias = true
..strokeWidth = 3.0
..style = PaintingStyle.fill
..color = Colors.white54
..invertColors = false;
final r = size.width / 2.0;
drawRing(canvas, paint, r);
}
/// 绘制土星环
void drawRing(Canvas canvas, Paint paint, double halfHeight) {
paint.color = ringColor;
paint.style = PaintingStyle.stroke;
paint.strokeWidth = ringWidth;
double smallCircleRadius = halfHeight / 2;
double height = halfHeight * 2;
canvas.drawArc(
new Rect.fromLTRB(smallCircleRadius, smallCircleRadius,
height - smallCircleRadius, height - smallCircleRadius),
-170 / 180 * pi, // ringStartRadian,
160 / 180 * pi, // ringRadian,
false,
paint);
canvas.drawArc(
new Rect.fromLTRB(smallCircleRadius, smallCircleRadius,
height - smallCircleRadius, height - smallCircleRadius),
-350 / 180 * pi, // ringStartRadian,
160 / 180 * pi, // ringRadian,
false,
paint);
}
@override
bool shouldRepaint(SaturnRingPainter oldDelegate) => true;
}
/// 生成土星按钮(两个在 icon 外围旋转的近半圆弧的按钮)
class SaturnButton extends StatefulWidget {
final icon;
final onPressed;
final tooltip;
final bool isAnimate;
final ringColor;
SaturnButton(
{Key? key,
this.icon,
this.tooltip,
this.onPressed,
this.ringColor = Colors.green,
required this.isAnimate});
@override
State<StatefulWidget> createState() => _SaturnButtonState();
}
class _SaturnButtonState extends State<SaturnButton>
with SingleTickerProviderStateMixin {
late AnimationController _animationController;
late Animation<double> _animation;
late Animation _colorTween;
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override
void initState() {
_animationController =
AnimationController(duration: Duration(seconds: 2), vsync: this);
_animationController.addStatusListener((status) {
if (status == AnimationStatus.completed) {
// controller.forward() 正向执行动画,结束时会回调此方法
// print("status is completed");
// 重置起点
_animationController.reset();
// 开启动画
_animationController.forward();
}
});
// 土星环动画
_animation = Tween(begin: .0, end: .5).animate(_animationController);
// 原按钮颜色动画
_colorTween = ColorTween(begin: Colors.white, end: widget.ringColor)
.animate(_animationController);
// 开始动画
_animationController.forward();
super.initState();
}
@override
Widget build(BuildContext context) {
return Stack(alignment: Alignment.center, children: [
Offstage(
offstage: !widget.isAnimate,
child: widget.isAnimate
? RotationTransition(
child: Container(
width: 50,
height: 50,
child: CustomPaint(
painter: SaturnRingPainter(ringColor: widget.ringColor),
)),
turns: _animation,
)
: null),
IconButton(
icon: widget.icon,
color: widget.isAnimate ? _colorTween.value : null,
onPressed: () {
widget.onPressed();
},
),
]);
}
}
SaturnButton 的使用与 IconButton 相似,只是参数有所不同,在此就不再展开了。