技术无止境,只怕不学习啊,Flutter 我们开始吧
CircleProgressBar原型进度条
自定义view结合动画来完成进度条效果。
CustomPainter
先来想想使用canvas的哪个方法来完成绘制。
首先,需要绘制一个圆形的背景啊,所以肯定要使用canvas.drawCircle方法。
其次,需要绘制圆上面的圆弧,所以就是canvas.drawArc方法了啊。
所以,先来绘制一个圆来看效果哈
/// 绘制进度条
class CircleProgressBarPainter extends CustomPainter {
var _paintBckGround;
CircleProgressBarPainter() {
_paintBckGround = new Paint()
..color = Colors.grey
..isAntiAlias = true
..strokeCap = StrokeCap.round
..strokeWidth = 10.0
..style = PaintingStyle.stroke;
}
@override
void paint(Canvas canvas, Size size) {
canvas.drawCircle(Offset(size.width/2, size.height/2), 100, _paintBckGround);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return false;
}
}
调用:
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.red, // 风格颜色
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: '测试项目'),
);
}
}
class MyHomePage extends StatefulWidget {
final String title;
MyHomePage({Key key, this.title}) : super(key: key);
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Container(
child: CustomPaint(
painter: CircleProgressBarPainter(),
),
),
),
);
}
}
/// 绘制进度条
class CircleProgressBarPainter extends CustomPainter {
var _paintBckGround;
var _paintFore;
CircleProgressBarPainter() {
_paintBckGround = new Paint()
..color = Colors.grey
..isAntiAlias = true
..strokeCap = StrokeCap.round
..strokeWidth = 10.0
..style = PaintingStyle.stroke;
_paintFore = new Paint()
..color = Colors.blueAccent
..isAntiAlias = true
..strokeWidth = 10.0
..strokeCap = StrokeCap.round
..style = PaintingStyle.stroke;
}
@override
void paint(Canvas canvas, Size size) {
//圆
canvas.drawCircle(Offset(0, 0), 100, _paintBckGround);
//半圆
Rect rect = Rect.fromCircle(center: Offset(0, 0), radius: 100);
canvas.drawArc(rect, 0, 3.14, false, _paintFore);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return false;
}
}
还是需要回到第一个问题,要有哪些功能,那些参数需要暴露出去
属性 | 作用 |
---|---|
_strokeWidth | 圆弧宽度 |
_backgroundColor | 进度条背景颜色 |
_foreColor | 进度条前景颜色 |
_startAngle | 进度开始的角度 |
_sweepAngle | 扫过的角度 |
_endAngle | 结束的角度 |
/// 绘制进度条
class CircleProgressBarPainter extends CustomPainter {
var _paintBckGround;
var _paintFore;
final _strokeWidth;
final _backgroundColor;
final _foreColor;
final _startAngle;
final _sweepAngle;
final _endAngle;
CircleProgressBarPainter(this._backgroundColor, this._foreColor,
this._startAngle, this._sweepAngle, this._endAngle, this._strokeWidth) {
_paintBckGround = new Paint()
..color = _backgroundColor
..isAntiAlias = true
..strokeCap = StrokeCap.round
..strokeWidth = _strokeWidth
..style = PaintingStyle.stroke;
_paintFore = new Paint()
..color = _foreColor
..isAntiAlias = true
..strokeWidth = _strokeWidth
..strokeCap = StrokeCap.round
..style = PaintingStyle.stroke;
}
@override
void paint(Canvas canvas, Size size) {
var radius = size.width > size.height ? size.width / 2 : size.height / 2;
//圆
canvas.drawCircle(Offset(radius, radius), radius, _paintBckGround);
//半圆
Rect rect = Rect.fromCircle(center: Offset(radius, radius), radius: radius);
canvas.drawArc(rect, _startAngle / 180 * 3.14, _sweepAngle / 180 * 3.14,
false, _paintFore);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return _sweepAngle != _endAngle;
}
}
调用:
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Container(
child: CustomPaint(
painter: CircleProgressBarPainter(
Colors.grey, Colors.redAccent, 0, 270.0, 360.0, 10.0),
size: Size(100, 200),
),
),
),
);
}
}
效果:
只要更改这里的参数,这个圆弧的显示就会改变。但是却不能动态改变,要想要动态改变还是需要借助于动画的。
结合动画 文字显示
首先建立CurvedAnimation使用减速的插值器来模拟减速效果。然后结合Animation实现数值的变化
class CircleProgressBarState extends State<CircleProgressBar>
with SingleTickerProviderStateMixin {
Animation<double> _doubleAnimation;
AnimationController _animationController;
CurvedAnimation curve;
@override
void initState() {
super.initState();
_animationController = new AnimationController(
vsync: this, duration: Duration(milliseconds: widget.duration));
curve = new CurvedAnimation(
parent: _animationController, curve: Curves.decelerate);
_doubleAnimation =
new Tween(begin: widget.startNumber, end: widget.maxNumber)
.animate(curve);
_animationController.addListener(() {
setState(() {});
});
onAnimationStart();
}
@override
void reassemble() {
onAnimationStart();
}
onAnimationStart() {
_animationController.forward(from: widget.startNumber);
}
@override
void dispose() {
super.dispose();
_animationController.dispose();
}
@override
Widget build(BuildContext context) {
var percent = (_doubleAnimation.value / widget.maxNumber * 100).round();
return Container(
width: widget.size,
height: widget.size,
child: CustomPaint(
painter: CircleProgressBarPainter(
widget.backgroundColor,
widget.foreColor,
widget.startNumber / widget.maxNumber * 360,
_doubleAnimation.value / widget.maxNumber * 360,
widget.maxNumber / widget.maxNumber * 360,
widget.strokeWidth),
size: Size(widget.size, widget.size),
child: Center(
child: Text(
"${_doubleAnimation.value.round() == widget.maxNumber ? "完成" : "${widget.textPercent ? "$percent%" : "${_doubleAnimation.value.round()}/${widget.maxNumber.round()}"}"}",
style: widget.textStyle == null
? TextStyle(color: Colors.black, fontSize: 20)
: widget.textStyle),
),
));
}
}
整体代码:
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.red, // 风格颜色
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: '进度条'),
);
}
}
class MyHomePage extends StatefulWidget {
final String title;
MyHomePage({Key key, this.title}) : super(key: key);
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Container(
// child: CustomPaint(
// painter: CircleProgressBarPainter(
// Colors.grey, Colors.redAccent, 0, 270.0, 360.0, 10.0),
// size: Size(100, 200),
// ),
child: CircleProgressBar(
200.0,
backgroundColor: Colors.grey,
foreColor: Colors.red,
startNumber: 0,
maxNumber: 100,
duration: 3000,
textPercent: true,
),
),
),
);
}
}
class CircleProgressBar extends StatefulWidget {
final Color backgroundColor;
final Color foreColor;
final int duration;
final double size;
final bool textPercent;
final double strokeWidth;
final double startNumber;
final double maxNumber;
final TextStyle textStyle;
const CircleProgressBar(
this.size, {
this.backgroundColor = Colors.grey,
this.foreColor = Colors.blueAccent,
this.duration = 3000,
this.strokeWidth = 10.0,
this.textStyle,
this.startNumber = 0.0,
this.maxNumber = 360,
this.textPercent = true,
});
@override
State<StatefulWidget> createState() {
return CircleProgressBarState();
}
}
class CircleProgressBarState extends State<CircleProgressBar>
with SingleTickerProviderStateMixin {
Animation<double> _doubleAnimation;
AnimationController _animationController;
CurvedAnimation curve;
@override
void initState() {
super.initState();
_animationController = new AnimationController(
vsync: this, duration: Duration(milliseconds: widget.duration));
curve = new CurvedAnimation(
parent: _animationController, curve: Curves.decelerate);
_doubleAnimation =
new Tween(begin: widget.startNumber, end: widget.maxNumber)
.animate(curve);
_animationController.addListener(() {
setState(() {});
});
onAnimationStart();
}
@override
void reassemble() {
onAnimationStart();
}
onAnimationStart() {
_animationController.forward(from: widget.startNumber);
}
@override
void dispose() {
super.dispose();
_animationController.dispose();
}
@override
Widget build(BuildContext context) {
var percent = (_doubleAnimation.value / widget.maxNumber * 100).round();
return Container(
width: widget.size,
height: widget.size,
child: CustomPaint(
painter: CircleProgressBarPainter(
widget.backgroundColor,
widget.foreColor,
widget.startNumber / widget.maxNumber * 360,
_doubleAnimation.value / widget.maxNumber * 360,
widget.maxNumber / widget.maxNumber * 360,
widget.strokeWidth),
size: Size(widget.size, widget.size),
child: Center(
child: Text(
"${_doubleAnimation.value.round() == widget.maxNumber ? "完成" : "${widget.textPercent ? "$percent%" : "${_doubleAnimation.value.round()}/${widget.maxNumber.round()}"}"}",
style: widget.textStyle == null
? TextStyle(color: Colors.black, fontSize: 20)
: widget.textStyle),
),
));
}
}
代码可以直接执行看效果,本节就到这里了。