当前位置: 代码迷 >> 综合 >> Flutter 浅析之 自定义view 六 CircleProgressBar
  详细解决方案

Flutter 浅析之 自定义view 六 CircleProgressBar

热度:100   发布时间:2023-09-28 22:40:21.0

技术无止境,只怕不学习啊,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(),),),),);}
}

效果:
Flutter 浅析之 自定义view 六 CircleProgressBar
然后,尝试在相同的位置再绘制一段圆弧

/// 绘制进度条
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;}
}

效果:
Flutter 浅析之 自定义view 六 CircleProgressBar
构造方法抽取

还是需要回到第一个问题,要有哪些功能,那些参数需要暴露出去

属性 作用
_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),),),),);}
}

效果:
Flutter 浅析之 自定义view 六 CircleProgressBar
只要更改这里的参数,这个圆弧的显示就会改变。但是却不能动态改变,要想要动态改变还是需要借助于动画的。

结合动画 文字显示
首先建立CurvedAnimation使用减速的插值器来模拟减速效果。然后结合Animation实现数值的变化

Flutter 浅析之 自定义view 六 CircleProgressBar

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),),));}
}

代码可以直接执行看效果,本节就到这里了。

  相关解决方案