推广

Flutter-绘制自定义仪表盘

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

仪表盘效果图

  • 仪表盘基本参数
    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://0291.com.cn/post/56553.html

    相关文章

    淘宝店铺释放后重开有影响吗(淘宝店铺彻底释放后还能再开吗)

    淘宝店铺释放后重开有影响吗(淘宝店铺彻底释放后还能再开吗)

    当然,如果店铺有违规的情况,那么也会存在重开店铺的记录中。另外所有的店铺名称都是独一无二的,而当你的淘宝店铺被释放之后,之前店铺的名称是不会得到保留的。...

    究竟什么是富人思维?

    究竟什么是富人思维?

    究竟什么是富人?...

    市场营销的精髓是什么?

    市场营销的精髓是什么?

    的精髓是什么?...

    浅谈线上运营推广的五个步骤

    浅谈线上运营推广的五个步骤

    运营推广在整个运营环节里面起到非常大的作用,同时也是压力非常大的一个职业,围绕着我们的只有KPI,数据。要想做好这个环节我们一定要具有很强大的抗压能力,沟通能力,执行能力,数据分析能力。所以在接手一个项目的时候,要仔细构思以下思路。 第一步:做好市场调查和分析 一个项目离不开的是市场和...

    为什么淘宝开店要交保证金(开淘宝店为什么要缴纳保证金)

    为什么淘宝开店要交保证金(开淘宝店为什么要缴纳保证金)

    考虑消费者的权益问题,比如假货、退货、辱骂客人,这样被判你的问题的话淘宝就会扣你的保证金,淘宝是平台,保证金相当于约束卖家的其中之一手段,但是交保证金是自愿的。...

    像我家一站式线上获客解决方案破解泛家居业渠道模式升级难题

    像我家一站式线上获客解决方案破解泛家居业渠道模式升级难题

    2020年受到疫情黑天鹅影响,传统家居业零售进一步被蚕食,线下零售终端也不再是消费者购买家居产品的唯一选择途径。同时,近年来房地产政策的收紧、互联网冲击,市场大环境的变化都在倒逼着家居企业、商加快转型升级的脚步。在消费者主权时代,该如何更好的触达消费者、破解获客难?数字经济...

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

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