技术无止境,只怕不学习啊,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;}@overridevoid paint(Canvas canvas, Size size) {
canvas.drawCircle(Offset(size.width/2, size.height/2), 100, _paintBckGround);}@overridebool shouldRepaint(CustomPainter oldDelegate) {
return false;}
}
调用:
void main() {
runApp(MyApp());
}class MyApp extends StatelessWidget {
// This widget is the root of your application.@overrideWidget 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> {
@overrideWidget build(BuildContext context) {
// TODO: implement buildreturn 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;}@overridevoid 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);}@overridebool 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;}@overridevoid 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);}@overridebool shouldRepaint(CustomPainter oldDelegate) {
return _sweepAngle != _endAngle;}
}
调用:
class _MyHomePageState extends State<MyHomePage> {
@overrideWidget build(BuildContext context) {
// TODO: implement buildreturn 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;@overridevoid 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();}@overridevoid reassemble() {
onAnimationStart();}onAnimationStart() {
_animationController.forward(from: widget.startNumber);}@overridevoid dispose() {
super.dispose();_animationController.dispose();}@overrideWidget 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.@overrideWidget 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 {
@overrideWidget build(BuildContext context) {
// TODO: implement buildreturn 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,});@overrideState<StatefulWidget> createState() {
return CircleProgressBarState();}
}class CircleProgressBarState extends State<CircleProgressBar>with SingleTickerProviderStateMixin {
Animation<double> _doubleAnimation;AnimationController _animationController;CurvedAnimation curve;@overridevoid 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();}@overridevoid reassemble() {
onAnimationStart();}onAnimationStart() {
_animationController.forward(from: widget.startNumber);}@overridevoid dispose() {
super.dispose();_animationController.dispose();}@overrideWidget 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),),));}
}
代码可以直接执行看效果,本节就到这里了。