推广

Flutter-绘制自定义仪表盘

iseeyu2年前 (2024-02-22)推广126

仪表盘效果图

  • 仪表盘基本参数
    min – 最小值
    max – 最大值
    progress – 进度值
    仪表盘总共300度,那么每个点的刻度为:300 / (max-min)

  • 效果图主要掌握如下,
    Canvas: 1. ARC绘制,2.Paragraph文本绘制,3.图形旋转;

  • 绘制方法:
    – _drawProgressArc 绘制扇形⭕️
    – _drawArcPointLine 绘制仪表盘刻度线
    – _drawArcProgressPoint 绘制仪表盘刻度文字

    • 用到的基本公式:
      1.角度转弧度:num _toRadius(num degree) => degree * Math.pi / 180;
      2.圆上的点坐标(x, y)求值:
      已知圆心坐标(ox, oy),半径为r,则圆上点坐标可如下求解,
      x = ox + r * cos(rad);
      y = oy + r * sin(rad);

    • 注意点:
      绘制文本,需要PragraphBuilder 执行 layout 方法。

    • 基本代码如下:

    import 'dart:math' as Math;
    import 'dart:ui' as UI;
    import 'package:flutter/material.dart';
    
    class ArcProgressBar extends StatelessWidget {
      final double width;
      final double height;
      final double min;
      final double max;
      final double progress;
    
      ArcProgressBar({
        Key key,
        this.width = 180,
        this.height = 180,
        this.min = 0,
        this.max = 100,
        this.progress = 0,
      }) : super(key: key);
    
      @override
      Widget build(BuildContext context) => AspectRatio(
          aspectRatio: 1,
          child: CustomPaint(
            size: Size(width, height),
            painter: _ArcProgressBarPainter(8, progress, min: min, max: max),
          ));
    }
    
    class _ArcProgressBarPainter extends CustomPainter {
      Paint _paint = Paint();
    
      double _strokeSize;
    
      double get _margin => _strokeSize / 2;
    
      num progress = 0;
    
      num min;
    
      num max;
    
      _ArcProgressBarPainter(double strokeSize, this.progress,
          {this.min = 0, this.max = 100}) {
        this._strokeSize = strokeSize;
        if (progress == null || progress < min) progress = 0;
        if (progress > max) progress = max;
        if (min == null || min <= 0) min = 0;
        if (max == null || max <= min) max = 100;
      }
    
      @override
      void paint(Canvas canvas, Size size) {
        num radius = size.width / 2;
        num cx = radius;
        num cy = radius;
        _drawProgressArc(canvas, size);
        _drawArcProgressPoint(canvas, cx, cy, radius);
        _drawArcPointLine(canvas, cx, cy, radius);
      }
    
      void _drawProgressArc(Canvas canvas, Size size) {
        _paint
          ..isAntiAlias = true
          ..color = Colors.grey[400]
          ..style = PaintingStyle.stroke
          ..strokeCap = StrokeCap.round
          ..strokeWidth = _strokeSize;
        canvas.drawArc(
            Rect.fromLTWH(_margin, _margin, size.width - _strokeSize,
                size.height - _strokeSize),
            _toRadius(120),
            _toRadius(300),
            false,
            _paint);
    
        _paint
          ..color = Colors.cyan
          ..strokeWidth = _strokeSize - 1;
        canvas.drawArc(
            Rect.fromLTWH(_margin + 1, _margin + 1, size.width - _strokeSize - 2,
                size.height - _strokeSize - 2),
            _toRadius(120),
            progress * _toRadius(300 / (max - min)),
            false,
            _paint);
      }
    
      void _drawArcProgressPoint(Canvas canvas, num cx, num cy, num radius) {
        _paint.strokeWidth = 1;
        canvas.save();
        canvas.translate(cx, cy);
        canvas.rotate(_toRadius(120));
        canvas.translate(-cx, -cy);
        for (int i = 0; i <= (max - min); i++) {
          num evaDegree = i * _toRadius(300 / (max - min));
          num b = i % 10 == 0 ? -5 : 0;
          num x = cx + (radius - 20 + b) * Math.cos(evaDegree);
          num y = cy + (radius - 20 + b) * Math.sin(evaDegree);
          num x1 = cx + (radius - 12) * Math.cos(evaDegree);
          num y1 = cx + (radius - 12) * Math.sin(evaDegree);
          canvas.drawLine(Offset(x, y), Offset(x1, y1), _paint);
        }
        canvas.translate(cx, cy);
        canvas.rotate(_toRadius(-120));
        canvas.translate(-cx, -cy);
        for (int i = min; i <= max; i += 10) {
          var pb = UI.ParagraphBuilder(
              UI.ParagraphStyle(fontSize: 15, textAlign: TextAlign.start))
            ..pushStyle(UI.TextStyle(color: Colors.black))
            ..addText(i.toString());
          UI.Paragraph p = pb.build()..layout(UI.ParagraphConstraints(width: 30));
          num evaDegree = _toRadius(120) + i * _toRadius(300 / (max - min));
          num x = cx + (radius - 40) * Math.cos(evaDegree);
          num y = cy + (radius - 40) * Math.sin(evaDegree);
          canvas.drawParagraph(p, Offset(x - 8, y - 10));
        }
        canvas.restore();
      }
    
      void _drawArcPointLine(UI.Canvas canvas, num cx, num cy, num radius) {
        canvas.save();
        canvas.translate(cx, cy);
        canvas.rotate(_toRadius(120));
        canvas.translate(-cx, -cy);
        _paint
          ..color = Colors.amber[800]
          ..style = PaintingStyle.fill
          ..strokeWidth = 3;
        num degree = _toRadius(300 / (max - min)) * progress;
        num x = cx + radius * 3 / 5 * Math.cos(degree);
        num y = cy + radius * 3 / 5 * Math.sin(degree);
        canvas.drawLine(Offset(cx, cy), Offset(x, y), _paint);
        _paint.color = Colors.amber[900];
        canvas.drawCircle(Offset(cx, cy), 12, _paint);
        canvas.restore();
      }
    
      num _toRadius(num degree) => degree * Math.pi / 180;
    
      @override
      bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
    }
    
    

    扫描二维码推送至手机访问。

    版权声明:本文由西安泽虎代运营发布,如需转载请注明出处。

    转载请注明出处https://www.0291.com.cn/post/56553.html

    相关文章

    郑州机场有序运行

    根据郑州市新冠肺炎疫情防控指挥部五月份最新通告要求,乘坐民航航班进出郑州新郑国际机场T2航站楼时,旅客需要根据郑州市疫情防控通告要求,提供相关手续和证明。目前,郑州机场有序运行。郑州机场大巴目前运行正常,其中从郑州市区到郑州机场,需要旅客至郑州市区(金水路民航大酒店)乘车点...

    我来教你公司网站建设怎么做才能吸引更多的用户。

    我来教你公司网站建设怎么做才能吸引更多的用户。

           网站对于一个公司来说还是很重要的,公司网站已经是每个企业对外宣传营销的名片了。很多公司也都纷纷的进行,那么公司网站建设怎样做才有吸引力? 1、网站主题 公司网站建设没有自己的主题,会让用户失去浏览的重心,然而充斥着不同主题内容,也会让整个...

    人一生如何积累财富?

    人的一生只有一次机会为退休做打算,如果退休之前,你没存下足够的钱,那将没办法重来,很多人对退休金犯下最大错误可能是太过看重安全性,在年轻时把资金过多的投入到银行存款中,失去了几十年积累复利的机会,其实对于现代人来说,即使你已经五六十岁,一般预期也还要再二三十年甚至更长时间,...

    闲鱼上卖东西怎么总是被下架(闲鱼违规之后,商品都下架了,怎么办)

    闲鱼上卖东西怎么总是被下架(闲鱼违规之后,商品都下架了,怎么办)

    闲鱼上卖东西总是被下架应该是1、发布了疑似违规的东西;2、短时间内发布了大量的商品;需要闲鱼卖家去熟悉闲鱼公约,如果遇到了不能确认的商品,可以问闲鱼客户确认!...

    《纳瓦尔宝典》关于财富与幸福的几点启示

    《纳瓦尔宝典》关于财富与幸福的几点启示

    最近刚读完《纳瓦尔宝典》,纳瓦尔思想独到,读完收获良多。本篇文章是我读后的一些个人思考,希望读文章的你可以有些许收获。财富篇纳瓦尔用了半本书的篇幅来讲述如何获取财富,他认为获取财富,最重要的是将自己产品化。(财富是指那些在自己睡觉时也能的资产,金钱只是转移财富的方式)我十分...

    如何防止网站SEO优化上首页之后被百度降权。

    如何防止网站SEO优化上首页之后被百度降权。

    SEO是获得网站访问量关键来源于之首,可是依据市SEO提升大资料显示,在70%以上的网站常有过被百度降权的亲身经历,这类亲身经历就就象大冬季上下班走在大街上被楼上住户倒了一棵冰冷的洗脚水,心里猛然......(此处省略万字),那麼在平时的SEO提升中怎样避免网址被百度降权?下边解读幾點关键要素。...

    现在,非常期待与您的又一次邂逅

    我们努力让每一部企业宣传片和抖音短视频成为商业大片