Flutter开发圆形计时进度条RingProgressBar

  • 演示

        先看效果图:

        

        由于无法截取动态图,我就截过程中的两张图片表达了,我想应该能看得懂。

  • 功能

        1.设置进度条半径

        2.设置进度条宽度

        3.设置进度条最大值

        4.设置进度条背景色以及前景色

        5.是否显示进度条文字

        6.文字样式设置

        7.点击进度条和进度条计时完成回调

        8.进度条是倒计时还是正计时设置

  •  代码
import 'dart:async';
import 'dart:math';

import 'package:flutter/material.dart';

class RingProgressBar extends StatefulWidget {
  /// 半径
  final double radius;

  /// 环颜色
  final Color? ringColor;

  /// 环背景颜色
  final Color? ringBgColor;

  /// 环中间文字
  final Color? textColor;

  /// 环中间文字大小
  final double? textSize;

  /// 环宽度
  final double strokeWidth;

  /// 是否显示环中间文本
  final bool isShowText;

  /// 环是否是倒计时,true:是倒计时,false:顺计时
  final bool? isCountDown;

  /// 计时截至值
  final int? maxProgress;

  final VoidCallback? callback;

  const RingProgressBar(
      {super.key,
      required this.radius,
      required this.strokeWidth,
      this.ringColor,
      this.ringBgColor,
      this.isShowText = false,
      this.textSize,
      this.textColor,
      this.isCountDown = true,
      this.maxProgress,
      this.callback});

  @override
  State<StatefulWidget> createState() => _RingProgressBarState();
}

class _RingProgressBarState extends State<RingProgressBar> {
  /// 进度条当前进度值
  double _value = 0;
  /// 进度条当前进度文本
  String _text = "0";
  /// 计时器
  Timer? timer;

  @override
  void initState() {
    super.initState();
    int count = 0;
    //计时器,每1毫秒执行一次
    const period = Duration(milliseconds: 1);
    timer = Timer.periodic(period, (timer) {
      count++;
      double max = (widget.maxProgress ?? 0) * 1000;
      //计时器结束条件
      if (widget.maxProgress == null ||
          widget.maxProgress == 0 ||
          count >= max) {
        timer.cancel();
        if (widget.callback != null) {
          //执行完成回调
          widget.callback!();
        }
      }
      //只有当widget状态为mounted时才执行setState防止内存泄露
      if (mounted) {
        setState(() {
          _value = count / max;
          _text = widget.isCountDown ?? true
              ? ((widget.maxProgress ?? 0) - (count ~/ 1000)).toString()
              : (count ~/ 1000).toString();
        });
      }
    });
  }

  @override
  void dispose() {
    //退出时关闭计时器防止内存泄露
    timer?.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return InkWell(
        highlightColor: Colors.transparent,
        splashColor: Colors.transparent,
        onTap: () {
          if (widget.callback != null) {
            //点击控件回调
            widget.callback!();
          }
        },
        child: Container(
          width: widget.radius * 2,
          height: widget.radius * 2,
          color: Colors.transparent,
          child: Stack(
            alignment: Alignment.center,
            fit: StackFit.expand,
            children: [
              CustomPaint(
                size: Size(widget.radius * 2, widget.radius * 2),
                painter: _RingPrinter(this, _value),
              ),
              Center(
                widthFactor: widget.radius * 2,
                heightFactor: widget.radius * 2,
                child: widget.isShowText
                    ? Text(
                        _text,
                        style: TextStyle(
                            color: widget.textColor, fontSize: widget.textSize),
                      )
                    : Container(),
              ),
            ],
          ),
        ));
  }
}

class _RingPrinter extends CustomPainter {
  /// state对象
  final _RingProgressBarState state;

  /// 控制值:0.0->1.0,会控制绘制0.0*2*pi->1.0*2*pi即从0开始绘制一个完整的圆
  final double _value;

  _RingPrinter(this.state, this._value);

  @override
  void paint(Canvas canvas, Size size) {
    //画笔
    Paint paint = Paint()
      ..color = state.widget.ringColor ?? Colors.transparent
      ..style = PaintingStyle.stroke
      ..strokeWidth = state.widget.strokeWidth
      ..isAntiAlias = true;
    //圆心偏移值
    double offset = state.widget.radius;
    //以offset为圆形,画半径减边线宽度一半为半径的圆
    Rect rect = Rect.fromCircle(
        center: Offset(offset, offset),
        radius: state.widget.radius - state.widget.strokeWidth / 2);
    paint.color = state.widget.ringBgColor ?? Colors.grey;
    //画圆背景
    canvas.drawCircle(Offset(offset, offset),
        state.widget.radius - state.widget.strokeWidth / 2, paint);
    paint.color = state.widget.ringColor ?? Colors.blueAccent;
    //让边界有弧形过渡
    paint.strokeCap = StrokeCap.round;
    //画进度条
    canvas.drawArc(rect, -0.5 * pi, _value * 2 * pi, false, paint);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }
}

关键代码 ,该代码可以控制从0.0*2.pi->1.0*2*pi从0开始绘制一个完整的圆,-0.5*pi这个参数是让进度条从12点钟方向开始绘制,系统默认从3点钟方向开始绘制。

//画进度条
canvas.drawArc(rect, -0.5 * pi, _value * 2 * pi, false, paint);

代码可以直接复制下来使用,注释非常完善,欢迎指正。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

许天成

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值