推广

Flutter-绘制自定义仪表盘

iseeyu3年前 (2024-02-22)推广132

仪表盘效果图

  • 仪表盘基本参数
    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

    相关文章

    西安企业做百度推广,一个月花三千和花三万到底差在哪?

    4|最近跟几个西安的老板聊天,发现一个挺有意思的现象:同样做百度推广,有人一个月花三千块,询盘不断;有人一个月砸三万,水花都没见着。问他们区别在哪,大多数人说不清楚,只知道'钱花了,没效果'。 5| 6|百度推广这东西,说复杂也复杂,说简单也简单。核心就一件事:让你的潜在客户搜某个词的时候,你的信息...

    我想当策划,大学应该选什么专业?

    我想当策划,大学应该选什么专业?

    学,广告,传播,公关,这些专业其实都可以。另外也可以学像汉语言文学,以及一些跟文学相关的一些专业,会对你找工作有帮助。但是,你想从事这个这份职业,除了专业的学习之外,更多的是,你要在上学的时候积极参与一些更营销策划相关的社会实践。比如说你可以做一些兼职,比如说帮别人去写稿,...

    服装市场营销策略分析,“定位不慎满盘皆输”

    服装市场营销策略分析,“定位不慎满盘皆输”

    不同的区域需要不同的策略,而不同的营销策略需要不同的销售思路来配合。因此,在研究女装产品销售思路之前,最好先对于市场与营销策略做一次深入地了解。1.市场区域化(market segmentation):目标营销的第一步,是将整个市场拆分成若干个同性质的小市场。2.目标市场的...

    晚间悦读丨你的思维方式,决定你的人生高度!

    晚间悦读丨你的思维方式,决定你的人生高度!

    越长大,越发现,能够决定你人生高度的,往往是你的认知层次。 你的认知在哪个层级,你的人生就处在什么状态。你认知的高度,决定了你人生的高度。 那如何才能升级方式、提升认知能力呢?可以从这三个方面做出改变。 NO. 1 改变态度 有人...

    编写项目营销策划方案六大要点

    编写项目营销策划方案六大要点

    现在商业发展非常迅速,的需求非常的大,但是要做好营销策划是有一定难度的,我把自己做营销策划的一些体会在这里分享给朋友们。营销策划方案六大要点我们先说项目营销策划方案这个话题。大部分的营销策划人都是在企业里工作,大部分项目之初都是来源于一个想法,创始人、老板或者创始团队的一个...

    【商场营销策划】如何进行商场营销策划?

    【商场营销策划】如何进行商场营销策划?

    商场是策划者们关注的永恒话题,那么究竟该如何作好商场营销策划呢?掌握以下这几点营销策略设计原则,至关重要!1.义利兼顾的原则。商场在获取利润的同时要充分考虑到消费者的合法利益和商场的长远利益。在作商场活动营销策略方案的时候应该将这一点作为首要的原则。对商场有利而对顾客不利的...

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

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