flutter 雷达图

news2024/11/16 11:36:48

通过CustomPainter自定义雷达图

效果如下

 主要代码

import 'package:flutter/material.dart';
import 'dart:math';
import 'dash_painter.dart';
import 'model/charts_model.dart';

class RadarChart extends StatelessWidget {
  final List<ChartModel> list;
  final double maxValue;
  final Color radarColor;
  final Color dataColor;

  const RadarChart({
    super.key,
    required this.list,
    this.maxValue = 100,
    this.radarColor = Colors.grey,
    this.dataColor = Colors.green,
  });

  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      painter: RadarChartPainter(
        list: list,
        maxValue: maxValue,
        radarColor: radarColor,
        dataColor: dataColor,
        numLayers: 4,
      ),
    );
  }
}

class RadarChartPainter extends CustomPainter {
  final int numLayers;
  final List<ChartModel> list;
  final double maxValue;
  final Color radarColor;
  final Color dataColor;
  List<Offset> startList = []; //存放第一层的点
  List<Offset> endList = []; //存放最外层的点
  List<Offset> textOffsetList = [];
  final int _offsetDy = 20;
  final int _offsetDx = 5;

  RadarChartPainter({
    required this.numLayers,
    required this.list,
    required this.maxValue,
    required this.radarColor,
    required this.dataColor,
  });

  @override
  void paint(Canvas canvas, Size size) {
    final center = Offset(size.width / 2, size.height / 2);
    final radius = size.width / 2;
    final dataPoints = <Offset>[];
    final radarPaint = Paint()
      ..color = radarColor
      ..style = PaintingStyle.stroke;
    final bgPaint = Paint()
      ..color = const Color(0xFFCAD0E8).withOpacity(.4)
      ..style = PaintingStyle.fill;
    //画背景颜色
    for (var layer = 1; layer <= numLayers; layer++) {
      if (layer == 3) {
        final layerRadius = radius * (layer / numLayers);
        final radarPath = Path();
        for (var i = 0; i < 6; i++) {
          //每一层的6个点
          final angle = (2 * pi / 6) * i - (pi / 2);
          final x = center.dx + layerRadius * cos(angle);
          final y = center.dy + layerRadius * sin(angle);
          final point = Offset(x, y);
          if (i == 0) {
            radarPath.moveTo(point.dx, point.dy);
          } else {
            radarPath.lineTo(point.dx, point.dy);
          }
        }
        radarPath.close();
        canvas.drawPath(radarPath, bgPaint);
      }
    }
    //连接每一层的6个点和文字
    for (var layer = 1; layer <= numLayers; layer++) {
      final layerRadius = radius * (layer / numLayers);
      final radarPath = Path();
      for (var i = 0; i < 6; i++) {
        final angle = (2 * pi / 6) * i - (pi / 2);
        final x = center.dx + layerRadius * cos(angle);
        final y = center.dy + layerRadius * sin(angle);
        final point = Offset(x, y);
        if (i == 0) {
          radarPath.moveTo(point.dx, point.dy);
        } else {
          radarPath.lineTo(point.dx, point.dy);
        }
        //保存第一层的点
        if (layer == 1) {
          startList.add(point);
        }
        //保存最外层的点,用于后面画第一层的最外层点的连线
        if (layer == 4) {
          endList.add(point);
          //画label文字
          const textStyle = TextStyle(fontSize: 12, fontWeight: FontWeight.bold, color: Color(0XFF999999));
          final textSpan = TextSpan(text: list[i].label, style: textStyle);
          final textPainter = TextPainter(
            text: textSpan,
            textDirection: TextDirection.ltr,
          );
          textPainter.layout();
          double w = textPainter.width;
          Offset off = const Offset(0, 0);
          if (i == 0) {
            off = Offset(point.dx - w * 0.5, point.dy - _offsetDy);
          } else if (i == 1 || i == 2) {
            off = Offset(point.dx + _offsetDx, point.dy);
          } else if (i == 4 || i == 5) {
            off = Offset(point.dx - _offsetDx - w, point.dy);
          } else if (i == 3) {
            off = Offset(point.dx - w * 0.5, point.dy);
          }
          textPainter.paint(canvas, off);
        }
      }
      radarPath.close();
      const DashPainter(span: 3, step: 3).paint(canvas, radarPath, radarPaint);
    }
    //画第一层的点到最外层的点的连线
    for (var i = 0; i < 6; i++) {
      final path = Path();
      path.moveTo(startList[i].dx, startList[i].dy);
      path.lineTo(endList[i].dx, endList[i].dy);
      const DashPainter(span: 4, step: 9).paint(canvas, path, radarPaint);
    }

    //画数据区域
    final dataPaint = Paint()
      ..color = dataColor
      ..style = PaintingStyle.stroke
      ..strokeWidth = 2.0;
    final dataFillPaint = Paint()
      ..color = dataColor.withOpacity(.3)
      ..style = PaintingStyle.fill
      ..strokeWidth = 2.0;

    final dataPath = Path();
    for (var i = 0; i < 6; i++) {
      final angle = (2 * pi / 6) * i - (pi / 2);
      final value = list[i].y;
      final normalizedValue = value / maxValue;
      final dataRadius = radius * 0.75 * normalizedValue + radius * 0.25;
      final x = center.dx + dataRadius * cos(angle);
      final y = center.dy + dataRadius * sin(angle);
      final point = Offset(x, y);

      if (i == 0) {
        dataPath.moveTo(point.dx, point.dy);
      } else {
        dataPath.lineTo(point.dx, point.dy);
      }

      dataPoints.add(point);
    }
    dataPath.close();
    canvas.drawPath(dataPath, dataFillPaint);
    canvas.drawPath(dataPath, dataPaint);

    final dataPointPaint = Paint()..color = dataColor;
    for (var point in dataPoints) {
      canvas.drawCircle(point, 4.0, dataPointPaint);
    }
  }

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

项目地址 : flutter_radar: flutter 雷达图

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

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

相关文章

微短剧赛道风口下的一站式点播解决方案

微短剧行业正风生水起。 一种全新的剧集模式正迅速崛起&#xff0c;并引起广泛关注。 从线下电影院的“巨幕”到PC端“网络大电影”&#xff0c;从“长视频”再到如今移动端1-3分钟的“微短剧”&#xff0c;影视行业在过去几年经历了一场深刻又显著的变化。 微短剧&#xff0…

【2023中国算力大会】《关于促进全国一体化算力网络国家枢纽节点宁夏枢纽建设若干政策的意见》解读

为加快人工智能的发展&#xff0c;丰富算力基础设施&#xff0c;推进算力赋能应用&#xff0c;强化绿电保障支持&#xff0c;构建算力产业体系&#xff0c;打造西部地区算力经济发展新高地。 对数据中心重大基础设施建设&#xff0c;每年安排专项债券资金给予支持&#xff0c;…

硬盘格式化恢复?仅仅4招,简单恢复数据!

“硬盘格式化之后还有恢复的可能性吗&#xff1f;不小心格式化硬盘之后真的超级后悔&#xff0c;现在想恢复里面的数据完全不知道应该怎么做&#xff01;真的很绝望&#xff01;帮帮我吧&#xff01; 硬盘作为计算机的主要存储设备&#xff0c;存储着大量的个人和工作数据。然而…

聊聊开关和CPU之间故事

我们本篇文章来看看计算机在硬件层面究竟是怎么表示二进制的&#xff0c;CPU究竟是怎么实现的&#xff1f;通过本文的学习&#xff0c;我们也可以反过来明白为什么计算机会采用二进制了。 开关 我们在生活中&#xff0c;处处都是开关&#xff0c;比如控制灯的开关 我们可以发…

Delphi 中 interface 如何使用 (chatGPT回答)?

目录 1. 定义接口&#xff1a;可以使用interface关键字来定义一个接口。例如&#xff1a; 2. 实现接口&#xff1a;类可以实现一个或多个接口。要实现接口&#xff0c;需要在类声明中使用implements关键字&#xff0c;并提供对应接口的方法的实现。例如&#xff1a; 3. 使用…

前端基础(props emit slot 父子组件间通信)

前言&#xff1a;如何实现组件的灵活使用&#xff0c;今天学习组件封装用到的props、slot和emit。 目录 props 子组件 父组件 示例代码 slot 示例代码 作用域插槽 emit 示例代码 props 需要实现在其他组件中使用同一个子组件。 子组件 子组件&#xff08;所谓子组件…

gpt-3.5-turbo-0613微调信用卡分期对话

1、数据准备&#xff1a;jsonl格式。样本&#xff1a;至少10个。d_train.jsonl {"messages": [{"role": "system", "content": "您是一位经验丰富的信用卡账单分期产品的销售助手。当前分期产品产品的特点是允许客户延期2个月开…

大数据项目实战(安装准备)

一&#xff0c;搭建大数据集群环境 1.1安装准备 1.1.1虚拟机安装与克隆 1.虚拟机的安装和设置以及启动虚拟机并安装操作系统&#xff08;以下仅供参考&#xff09; 安装一台虚拟机主机名为&#xff1a;hadoop01的虚拟机备用 VMware虚拟机安装Linux教程(超详细)_vmware安装…

呈现数据的精妙之道:选择合适的可视化方法

在当今数据时代&#xff0c;数据可视化已成为理解和传达信息的重要手段。然而&#xff0c;选择适合的数据可视化方法对于有效地呈现数据至关重要。不同的数据和目标需要不同的可视化方法&#xff0c;下面我们将探讨如何选择最佳的数据可视化方法来呈现数据。 1. 理解数据类型&a…

Putty连接登录Linux centos7

一、下载Putty 下载网址 https://www.putty.org/ 安装完成后会有两个重要的软件 Putty是用于连接Linux的客户端 PuTTYgen是通过客户端下载的service.ppk生成本地登录要用到的key 二、运行putty 双击putty.exe可以打开程序&#xff08;程序小&#xff0c;学习linux可以把该…

HarmonyOS开发:超详细了解项目的工程结构

前言 系列文章目录&#xff1a; HarmonyOS开发第一步&#xff0c;熟知开发工具DevEco Studio 当我们熟练的掌握了DevEco Studio之后&#xff0c;就可以创建项目进行练习了&#xff0c;和市场上大多数IDE一样&#xff0c;DevEco Studio也给我们提供了很多的实例模板&#xff0c…

0821|C++day1 初步认识C++

一、思维导图 二、知识点回顾 【1】QT软件的使用 1&#xff09;创建文件 创建文件时&#xff0c;文件的路径一定是全英文 2&#xff09;修改编码 工具--->选项--->行为--->默认编码&#xff1a;system 【2】C和C的区别 C又叫C plus plus&#xff0c;C是对C的扩充&…

videojs 实现自定义组件(视频画质/清晰度切换) React

前言 最近使用videojs作为视频处理第三方库&#xff0c;用来对接m3u8视频类型。这里总结一下自定义组件遇到的问题及实现&#xff0c;目前看了许多文章也不全&#xff0c;官方文档写的也不是很详细&#xff0c;自己摸索了一段时间陆陆续续完成了&#xff0c;这是实现后的效果.…

几何对象操作(一)

几何对象是构成要素的必要条件 import numpy as npdef getgtypes():return point, linestring, polygon, multipoint, multilinestring, multipolygondef showgeoms(geoms, name"tmp", gtypeNone):# 快速显示一串几何对象,gtype 如果是默认的则根据geoms生成相应的类…

《中国综合算力指数》发布,我国算力规模居全球第二,中创积极响应东数西算政策

算力通俗地说就是计算能力&#xff0c;是数据中心处理数据并输出结果的能力。2023中国算力大会于近期在宁夏银川开幕。工业和信息化部负责人在会上透露&#xff0c;我国算力产业已初具规模。 截至目前&#xff0c;全国在用数据中心机架总规模超过760万标准机架&#xff0c;算力…

【PHP】错误处理

文章目录 错误处理错误分类错误代号错误触发 错误处理 错误处理&#xff1a;指的是系统&#xff08;或者用户&#xff09;在对某些代码进行执行的时候&#xff0c;发现有错误&#xff0c;就会通过错误处理的形式告知程序员。 错误分类 1&#xff09;语法错误&#xff1a;用户…

IDEA配置JDK8、JDK17中文文档

1.获取对应的chm文件&#xff0c;如 JAVA_API_1.7中文.chm。 2.使用CHMDecoder工具,下载链接 https://download.cnet.com/CHM-Decoder/3000-20412_4-10390773.html 这种也有在线的&#xff0c;转化就行&#xff08;转化后是一个文件夹&#xff09; 如果是用的我说的这个工具…

I2C设备驱动挂载

一、 概述&#xff1a; I2C工作原理&#xff1a; I2C总线标准的两根传输线&#xff0c;SDA是数据线&#xff0c;Scl是时钟线&#xff0c;当SCL为高&#xff0c;SDA由高到低时&#xff0c;发送启动信息&#xff0c;发送9个脉冲&#xff0c;1-7是地址&#xff0c;8是读写控制位&a…

GEE/PIE 遥感大数据处理与典型案例

查看原文>>>【399三天】GEE/PIE遥感大数据处理与典型案例实践 随着航空、航天、近地空间等多个遥感平台的不断发展&#xff0c;近年来遥感技术突飞猛进。由此&#xff0c;遥感数据的空间、时间、光谱分辨率不断提高&#xff0c;数据量也大幅增长&#xff0c;使其越来…

FPGA GTX全网最细讲解,aurora 8b/10b协议,OV5640摄像头视频传输,提供2套工程源码和技术支持

目录 1、前言免责声明 2、我这里已有的 GT 高速接口解决方案3、GTX 全网最细解读GTX 基本结构GTX 发送和接收处理流程GTX 的参考时钟GTX 发送接口GTX 接收接口GTX IP核调用和使用 4、设计思路框架视频源选择OV5640摄像头配置及采集动态彩条视频数据组包GTX aurora 8b/10b数据对…