Flutter开发圆形计时进度条RingProgressBar

news2025/1/19 2:55:09
  • 演示

        先看效果图:

        

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

  • 功能

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/386983.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

机器学习100天(三十七):037 朴素贝叶斯-挑个好西瓜!

《机器学习100天》完整目录:目录 机器学习100天,今天讲的是:朴素贝叶斯-挑个好西瓜! 红色石头已经了解了贝叶斯定理和朴素贝叶斯法,接下来已经可以很自信地去买瓜了。买瓜之前,还有一件事情要做,就是搜集样本数据。红色石头通过网上资料和查阅,获得了一组包含 10 组样…

打印名片-课后程序(Python程序开发案例教程-黑马程序员编著-第二章-课后作业)

实例3&#xff1a;文本进度条 进度条以动态方式实时显示计算机处理任务时的进度&#xff0c;它一般由已完成任务量与剩余未完成任务量的大小组成。本实例要求编写程序&#xff0c;实现图1所示的进度条动态显示的效果。 下载中下载完成图1文本进度条 实例分析 在本实例中可以将…

【java】alibaba Fastjson --全解史上最快的JSON解析库

文章目录前序Fastjson 简介Fastjson 的优点速度快使用广泛测试完备使用简单功能完备下载和使用将 Java 对象转换为 JSON 格式JSONField创建 JSON 对象JSON 字符串转换为 Java 对象使用 ContextValueFilter 配置 JSON 转换使用 NameFilter 和 SerializeConfigFastjson 处理日期F…

如何使用SaleSmartly进行Facebook Messenger 营销、销售和支持

如何使用SaleSmartly&#xff08;ss客服&#xff09;进行Facebook Messenger 营销、销售和支持上篇文章我们讲了什么是Facebook Messenger CRM以及获得Facebook Messenger CRM的注意事项&#xff0c;现在你有更多时间与客户聊天&#xff0c;让我们看看你如何使用SaleSmartly&am…

缓存穿透和缓存击穿、缓存雪崩

一、Redis作为一个缓存中间件是如何工作的&#xff1f;架构图如下过程如下 客户端发起一个查询请求的时候&#xff0c;首先去缓存中查询&#xff0c;如果数据在缓存中存在&#xff0c;则直接将缓存中数据返回给客户端&#xff1b;如果数据在缓存中不存在&#xff0c;则继续查询…

嵌入式知识点-SPI通讯

该文原自 &#xff1a; 正点原子 01 SPI概述 SPI (Serial Peripheralinterface),顾名思义就是串行外围设备接口。SPI是一种高速的&#xff0c;全双工&#xff0c;同步的通信总线&#xff0c;并且在芯片的管脚上只占用四根线&#xff0c;节约了芯片的管脚&#xff0c;同…

网络营销培训完能达到什么水平?学完能创业吗?

网络营销本身就是一门创业的技术&#xff0c;很多人学习网络营销&#xff0c;往往担心学完以后技术达不到&#xff0c;再工作几年才可以创业&#xff0c;实际这是错误的理解&#xff0c;那么&#xff0c;网络营销培训完能达到什么水平&#xff1f;新手学员参加网络营销培训&…

MAX 10 10M50 FPGA(10M50DDF256I7G)10M50DDF484C8G/10M50DDF484I7G

MAX 10器件是单芯片、非易失性低成本可编程逻辑器件(pld)&#xff0c;用于集成最优的系统组件集。MAX 10设备的亮点包括&#xff1a;内部存储双配置闪存用户闪存即时支持集成模数转换器(adc)支持Nios II单芯片软核处理器该器件设备是系统管理、I/O扩展、通信控制平面、工业、汽…

Spark BlockManager数据存储与管理机制

BlockManager是整个Spark底层负责数据存储与管理的一个组件&#xff0c;Driver和Executor的所有数据都由对应的BlockManager进行管理。 Driver上有BlockManagerMaster&#xff0c;负责对各个节点上的BlockManager内部管理的数据的元数据进行维护&#xff0c;比如block的增删改等…

SpringBoot如何自定义一个starter

SpringBoot starter&#xff0c;大家应该在平常写项目中应该非常熟悉&#xff0c;很多依赖都会提供集成SpringBoot的依赖&#xff0c;这样我们用起来就非常顺手&#xff0c;开箱就能用&#xff0c;那如何自定义一个starter呢&#xff1f; SpringBoot starter SpringBoot中的一…

【C++初阶】2. 类和对象_1

1. 面向过程和面向对象的初步认识 2. 类的引入 C语言结构体中只能定义变量&#xff0c;在C中&#xff0c;结构体内不仅可以定义变量&#xff0c;也可以定义函数。比如&#xff1a; 之前在数据结构初阶中&#xff0c;用C语言方式实现的栈&#xff0c;结构体中只能定义变量&#…

Matlab深度学习实战二:AlexNet图像分类篇提供花分类这里以分二类演示且matlab提供模型框架详细操作流程

1.花数据集简介下载与准备 2.matlab搭建模型 3.matlab软件的操作过程&#xff1a; &#xff08;1&#xff09;界面操作 &#xff08;2&#xff09;深度学习设计器使用 &#xff08;3&#xff09;图像数据导入 &#xff08;4&#xff09;训练可视化 一、花数据集简介下载与准备…

学生档案管理系统的设计与实现

技术&#xff1a;Java、JSP等摘要&#xff1a;本设计是为托普学院学生档案的管理实现电子化而设计的&#xff0c;系统开发采用J2EE技术&#xff0c;数据库采用了SQL Server 2005&#xff0c;因而系统具有很好的扩展性、可移植性&#xff0c;实现了教学资源的信息化管理。主要功…

【Python学习笔记】第二十七节 Python 多线程

一、进程和线程进程&#xff1a;是程序的一次执行&#xff0c;每个进程都有自己的地址空间、内存、数据栈及其他记录运行轨迹的辅助数据。线程&#xff1a;所有的线程都运行在同一个进程当中&#xff0c;共享相同的运行环境。线程有开始、顺序执行和结束三个部分&#xff0c; …

CANFDNET-200U-UDP配置与数据收发控制

一、启动ZCANPRP,打开设备管理页面&#xff0c;选择类型CANFDNET-200U-UDP,如图1 图1 二、打开设备&#xff0c;启动&#xff0c;在相应页面如图2&#xff0c;配置协议&#xff0c;CANFD 加速&#xff0c;本地端口&#xff0c;IP地址&#xff0c;工作端口。 图2 三、发送相应数…

单元测试框架Mockito落地实践分享

一、序言 针对功能做测试的时候&#xff0c;我们经常会有单元测试和集成测试&#xff0c;在实际开发过程中发现有很多童鞋经常混淆这两个内容&#xff0c;在分享Mockito使用过程前先区分这两个概念。 二、测试分类和区别 所谓单元测试&#xff0c;其实就是对单个方法内部逻辑…

Nacos 2.2.0支持postgresql数据库

github地址&#xff1a;个人仓库本文基于扩展源码的方式进行的集成&#xff0c;官方推荐的方式为&#xff1a;扩展插件包源码修改1.1根pom增加postgresql依赖<postgresql.version>42.5.1</postgresql.version><dependency><groupId>org.postgresql<…

C语言数组详解

写在前面 在初识C语言的博客中我们已经知道什么是数组了,并且可以基本的使用,今天我们来详细的谈谈数组是什么,并且实现两个比较好玩的小程序. 数组 数组是什么?C语言中给了数组的定义:一组相同类型元素的集合.我们已经在初始C语言那里已经说过了.我们把下面的一个连续的空…

【全网最细PAT题解】【PAT乙】1049 数列的片段和(思路详细解释)

题目链接 1049 数列的片段和 题目描述 给定一个正数数列&#xff0c;我们可以从中截取任意的连续的几个数&#xff0c;称为片段。例如&#xff0c;给定数列 { 0.1, 0.2, 0.3, 0.4 }&#xff0c;我们有 (0.1) (0.1, 0.2) (0.1, 0.2, 0.3) (0.1, 0.2, 0.3, 0.4) (0.2) (0.2, 0.3)…

首发,pm3包,一个用于多组(3组)倾向评分匹配的R包

目前&#xff0c;本人写的第二个R包pm3包已经正式在CRAN上线&#xff0c;用于3组倾向评分匹配&#xff0c;只能3组不能多也不能少。 可以使用以下代码安装 install.packages("pm3")什么是倾向性评分匹配&#xff1f;倾向评分匹配&#xff08;Propensity Score Match…