Flutter 浅析之 自定义view 六 CircleProgressBar

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

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

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

效果:
Flutter 浅析之 自定义view 六 CircleProgressBar_第2张图片
构造方法抽取

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

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

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

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

Flutter 浅析之 自定义view 六 CircleProgressBar_第4张图片

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

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

你可能感兴趣的:(flutter,Android,IOS)