一、动画代码
1.成功动画
import 'package:flutter/material.dart';
import 'dart:ui';
import 'dart:math' as Math;
/*成功*/
class TickSuccess extends StatefulWidget {
final double width;
final Color color;
final double lineWidth;
final int animationPeriod; // 动画播放周期(影响动画速度)
TickSuccess({
Key key,
this.width = 200,
this.color,
this.lineWidth,
this.animationPeriod = 2000,
}) : super(key: key);
@override
_TickSuccessState createState() => _TickSuccessState();
}
class _TickSuccessState extends State<TickSuccess>
with TickerProviderStateMixin {
AnimationController _animationController;
Animation<double> _animation;
@override
void initState() {
super.initState();
/// AnimationController在给定的时间段内线性的生成从0.0到1.0(默认区间)的数字,总时长
_animationController = AnimationController(
vsync: this,
duration: Duration(milliseconds: widget.animationPeriod),
);
Animation curveAnimation =
new CurvedAnimation(parent: _animationController, curve: Curves.ease);
/// 设置输出范围值,这里分两个动画步骤,0 -> 1表示画圆, 1 -> 1.5 表示画钩
_animation = Tween<double>(begin: 0.0, end: 1.5).animate(curveAnimation);
_animationController.addListener(() {
setState(() {});
});
/// 开始动画
_animationController.forward();
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
width: widget.width,
height: widget.width,
child: CustomPaint(
painter: _TickSuccessPainter(_animation.value,
color: widget.color, lineWidth: widget.lineWidth),
),
);
}
}
class _TickSuccessPainter extends CustomPainter {
Paint viewPaint;
Path path;
final double progress;
final Color color;
final double lineWidth;
_TickSuccessPainter(this.progress, {this.color, this.lineWidth}) {
viewPaint = Paint()
..strokeCap = StrokeCap.round
..isAntiAlias = true
..color = color ?? Colors.green
..style = PaintingStyle.stroke
..strokeWidth = lineWidth ?? 5.0;
path = Path();
}
// Method to convert degree to radians
num degToRad(num deg) => deg * (Math.pi / 180.0);
@override
void paint(Canvas canvas, Size size) {
/// 原点x,y坐标
double x = size.width / 2;
double y = size.width / 2;
double radius = size.width / 3;
double len = 1 * (-radius / 6) + radius / 2;
path.moveTo(x, y);
/// 背景圆
path.addArc(
Rect.fromCenter(
center: Offset(x, y),
width: radius * 2,
height: radius * 2,
),
degToRad(0),
degToRad(360));
/// 打钩
path.moveTo(x - radius / 2, y);
path.lineTo(x - radius / 6, y + len);
path.lineTo(x + radius * 1 / 2, y - radius * 1 / 3);
/// 路径动画
PathMetrics pathMetrics = path.computeMetrics();
List list = pathMetrics.toList(); //数组长度与执行moveTo次数相关
/// 画圆
PathMetric circularPathMetric = list[0];
Path circularExtractPath = circularPathMetric.extractPath(
0.0,
circularPathMetric.length * progress,
);
canvas.drawPath(circularExtractPath, viewPaint);
/// 画钩
PathMetric tickPathMetric = list[1];
Path tickExtractPath = tickPathMetric.extractPath(
0.0,
tickPathMetric.length * (progress - 0.5),
);
canvas.drawPath(tickExtractPath, viewPaint);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
2.失败动画
import 'package:flutter/material.dart';
import 'dart:ui';
import 'dart:math' as Math;
/*失败*/
class CrossFail extends StatefulWidget {
final double width;
final Color color;
final double lineWidth;
final int animationPeriod; // 动画播放周期(影响动画速度)
CrossFail(
{Key key,
this.width = 200,
this.color,
this.lineWidth,
this.animationPeriod = 2000})
: super(key: key);
@override
_CrossFailState createState() => _CrossFailState();
}
class _CrossFailState extends State<CrossFail> with TickerProviderStateMixin {
AnimationController _animationController;
Animation<double> _animation;
@override
void initState() {
super.initState();
/// AnimationController在给定的时间段内线性的生成从0.0到1.0(默认区间)的数字,总时长
_animationController = AnimationController(
vsync: this,
duration: Duration(milliseconds: widget.animationPeriod),
);
Animation curveAnimation =
new CurvedAnimation(parent: _animationController, curve: Curves.ease);
/// 设置输出范围值,这里分两个动画步骤,0 -> 1表示画圆, 1 -> 1.5 表示画叉
_animation = Tween<double>(begin: 0.0, end: 1.5).animate(curveAnimation);
_animationController.addListener(() {
setState(() {});
});
/// 开始动画
_animationController.forward();
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
width: widget.width,
height: widget.width,
child: CustomPaint(
painter: _CrossFailPainter(_animation.value,
color: widget.color, lineWidth: widget.lineWidth),
));
}
}
class _CrossFailPainter extends CustomPainter {
Paint viewPaint;
Path path;
final double progress;
final Color color;
final double lineWidth;
_CrossFailPainter(this.progress, {this.color, this.lineWidth}) {
viewPaint = Paint()
..strokeCap = StrokeCap.round
..isAntiAlias = true
..color = color ?? Colors.red
..style = PaintingStyle.stroke
..strokeWidth = lineWidth ?? 5.0;
path = Path();
}
// Method to convert degree to radians
num degToRad(num deg) => deg * (Math.pi / 180.0);
@override
void paint(Canvas canvas, Size size) {
/// 原点x,y坐标
double x = size.width / 2;
double y = size.width / 2;
double radius = size.width / 3;
path.moveTo(x, y);
/// 背景圆
path.addArc(
Rect.fromCenter(
center: Offset(x, y),
width: radius * 2,
height: radius * 2,
),
degToRad(0),
degToRad(360));
path.moveTo(x - radius / 2, x - radius / 2);
path.lineTo(x + radius / 2, x + radius / 2);
path.moveTo(x + radius / 2, x - radius / 2);
path.lineTo(x - radius / 2, x + radius / 2);
/// 路径动画
PathMetrics pathMetrics = path.computeMetrics();
List list = pathMetrics.toList();
/// 画圆
PathMetric circularPathMetric = list[0];
Path circularExtractPath = circularPathMetric.extractPath(
0.0,
circularPathMetric.length * progress,
);
canvas.drawPath(circularExtractPath, viewPaint);
/// 画叉
PathMetric tickPathMetric = list[1];
Path tickExtractPath = tickPathMetric.extractPath(
0.0,
tickPathMetric.length * (progress - 0.5),
);
canvas.drawPath(tickExtractPath, viewPaint);
PathMetric linePathMetric = list[2];
Path lineExtractPath = linePathMetric.extractPath(
0.0,
linePathMetric.length * progress,
);
canvas.drawPath(lineExtractPath, viewPaint);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
二、使用
import 'package:flutter/material.dart';
import '../../compontents/animation/cross_fail.dart';
import '../../compontents/animation/tick_success.dart';
/*组件演示*/
class Demo extends StatefulWidget {
Demo({Key key}) : super(key: key);
@override
State<Demo> createState() => _DemoState();
}
class _DemoState extends State<Demo> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
margin: EdgeInsets.all(40),
child: Wrap(
spacing: 20,
crossAxisAlignment: WrapCrossAlignment.center,
children: <Widget>[
TickSuccess(width: 40),
TickSuccess(
width: 80,
color: Colors.green[700],
animationPeriod: 3000,
),
TickSuccess(
width: 120,
color: Colors.green[900],
lineWidth: 10,
animationPeriod: 4000,
),
CrossFail(width: 40),
CrossFail(
width: 80,
color: Colors.red[700],
animationPeriod: 3000,
),
CrossFail(
width: 120,
color: Colors.red[900],
lineWidth: 10,
animationPeriod: 4000,
),
]),
));
}
}
三、动画演示