flutter的自定义系列雷达图

news2024/11/25 6:47:57

自定义是flutter进阶中不可缺少的ui层知识点,这里我们来总结下:

在Flutter中,自定义绘制通常是通过使用CustomPaintCustomPainter来实现的。

  1. 创建CustomPaint组件

首先,需要创建一个CustomPaint组件。CustomPaint是一个Widget,它可以作为其他组件的子组件,用于提供自定义绘制的功能。创建CustomPaint时可以指定其子组件、前景画笔和背景画笔。

示例代码:

CustomPaint(
  painter: BackgroundPainter(),
  foregroundPainter: ForegroundPainter(),
  child: Container(),
)

这里BackgroundPainterForegroundPainter是自定义的画笔类,需要继承CustomPainter

  1. 创建自定义画笔类

接下来,需要创建自定义的画笔类。自定义画笔类需要继承CustomPainter类,并重写paintshouldRepaint方法。paint方法用于实现绘制逻辑,而shouldRepaint方法用于决定在什么情况下需要重绘。

示例代码:

class BackgroundPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    // 绘制逻辑
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    // 是否需要重绘
    return true;
  }
}
  1. 实现绘制逻辑

paint方法中,可以实现自定义的绘制逻辑。paint方法接收两个参数:Canvas对象和Size对象。Canvas对象提供了各种绘制方法,如绘制线、矩形、圆、文本等;Size对象表示要绘制的区域的大小。

示例代码:

void paint(Canvas canvas, Size size) {
  Paint paint = Paint()
    ..color = Colors.red
    ..strokeWidth = 5.0
    ..style = PaintingStyle.stroke;

  canvas.drawLine(Offset(0, 0), Offset(size.width, size.height), paint);
  canvas.drawRect(Rect.fromLTWH(0, 0, size.width / 2, size.height / 2), paint);
  canvas.drawCircle(Offset(size.width / 2, size.height / 2), 50, paint);
}
  1. 实现shouldRepaint方法

shouldRepaint方法用于决定在什么情况下需要重绘。通常,如果画笔的属性或者绘制数据发生改变时,需要返回true以触发重绘。否则,返回false以避免不必要的重绘。

示例代码:

bool shouldRepaint(CustomPainter oldDelegate) {
  // 可以根据具体情况判断是否需要重绘
  return oldDelegate != this;
}
  1. 添加动画和手势

根据需要,还可以在自定义绘制中添加动画和手势支持。例如,可以使用AnimationControllerTween来创建动画,并在paint方法中根据动画值绘制;可以使用GestureDetector来监听手势事件,并根据手势改变绘制。

总结一下,Flutter中的自定义绘制主要是通过创建CustomPaint组件和自定义画笔类来实现的。在自定义画笔类中,需要重写paintshouldRepaint方法来实现绘制逻辑和判断是否需要重绘。此外,还可以根据需求添加动画和手势支持。

下面我们就一起来实现一个自定义雷达图。

  1. 首先,引入所需的库:
import 'dart:math';
import 'package:flutter/material.dart';
  1. 创建一个自定义的雷达图组件RadarChart
class RadarChart extends StatelessWidget {
  final int numSides; // 边的数量
  final List<double> values; // 每个角上的数值
  final double maxValue; // 最大值,用于归一化数值

  RadarChart({required this.numSides, required this.values, this.maxValue = 100.0});

  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      painter: RadarChartPainter(numSides: numSides, values: values, maxValue: maxValue),
      child: Container(),
    );
  }
}
  1. 创建自定义绘制类RadarChartPainter,继承自CustomPainter
import 'dart:math';

import 'package:flutter/material.dart';

class RadarChartPainter extends CustomPainter {
  final int numSides;
  final List<double> values;
  final double maxValue;

  RadarChartPainter(
      {required this.numSides, required this.values, this.maxValue = 100.0});

  @override
  void paint(Canvas canvas, Size size) {
    final center = Offset(size.width / 2, size.height / 2);
    final radius = min(size.width / 2, size.height / 2);

    // 绘制雷达背景
    drawRadarBackground(canvas, center, radius);
    // 绘制雷达网格
    drawRadarGrid(canvas, center, radius);
    // 绘制雷达数据
    drawRadarData(canvas, center, radius);
    // 绘制数值文本
    drawTextLabels(canvas, center, radius);
  }

  void drawRadarBackground(Canvas canvas, Offset center, double radius) {
    final bgPaint = Paint()
      ..color = Colors.grey.withOpacity(0.3)
      ..style = PaintingStyle.stroke
      ..strokeWidth = 1.0;

    final angleStep = 2 * pi / numSides;
    for (int i = 0; i < numSides; i++) {
      final x = center.dx + radius * cos(i * angleStep);
      final y = center.dy + radius * sin(i * angleStep);
      canvas.drawLine(center, Offset(x, y), bgPaint);
    }
  }

  void drawRadarGrid(Canvas canvas, Offset center, double radius) {
    final gridPaint = Paint()
      ..color = Colors.grey.withOpacity(0.3)
      ..style = PaintingStyle.stroke
      ..strokeWidth = 0.5;

    final int gridLevel = 5; // 网格层数
    final angleStep = 2 * pi / numSides;
    final double gridRadiusStep = radius / gridLevel;

    for (int i = 1; i <= gridLevel; i++) {
      final currentRadius = gridRadiusStep * i;
      final path = Path();
      for (int j = 0; j < numSides; j++) {
        final x = center.dx + currentRadius * cos(j * angleStep);
        final y = center.dy + currentRadius * sin(j * angleStep);

        if (j == 0) {
          path.moveTo(x, y);
        } else {
          path.lineTo(x, y);
        }
      }
      path.close();
      canvas.drawPath(path, gridPaint);
    }
  }

  void drawRadarData(Canvas canvas, Offset center, double radius) {
    final dataPaint = Paint()
      ..color = Colors.blue
      ..style = PaintingStyle.fill;

    final path = Path();
    final angleStep = 2 * pi / numSides;
    for (int i = 0; i < numSides; i++) {
      final normalizedValue = values[i] / maxValue;
      final x = center.dx + radius * normalizedValue * cos(i * angleStep);
      final y = center.dy + radius * normalizedValue * sin(i * angleStep);

      if (i == 0) {
        path.moveTo(x, y);
      } else {
        path.lineTo(x, y);
      }
    }
    path.close();
    canvas.drawPath(path, dataPaint);
  }

  void drawTextLabels(Canvas canvas, Offset center, double radius) {
    final textPainter = TextPainter(textDirection: TextDirection.ltr);

    final angleStep = 2 * pi / numSides;
    for (int i = 0; i < numSides; i++) {
      final value = values[i].toStringAsFixed(1);
      final x = center.dx + radius * cos(i * angleStep);
      final y = center.dy + radius * sin(i * angleStep);

      textPainter.text = TextSpan(
          text: value, style: TextStyle(color: Colors.black, fontSize: 12.0));
      textPainter.layout();
      textPainter.paint(canvas,
          Offset(x - textPainter.width / 2, y - textPainter.height / 2));
    }
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }
}
  1. 使用RadarChart组件:
void main() {
  runApp(
    MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Radar Chart')),
        body: Center(
          child: SizedBox(
            width: 300,
            height: 300,
            child: RadarChart(numSides: 5, values: [50, 70, 90, 60, 80]),
          ),
        ),
      ),
    ),
  );
}

我们创建了一个RadarChart组件,可以自定义边的数量和每个角上的数值。

通过自定义RadarChartPainter类,我们实现了绘制雷达背景、雷达数据和数值文本的功能。

看看我们实现的效果:

微信图片_20230609101350.png

老子讲:“天下难事必作于易,天下大事必作于细。”,你我共勉之!

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

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

相关文章

MobPush 厂商通道申请指南

华为厂商申请 创建应用 登录华为开发者联盟&#xff0c;注册您的应用&#xff0c;在应用信息中获取APP ID和Client Secret 配置SHA256证书指纹 在华为开发者联盟配置SHA256证书指纹。获取及配置请参见华为官方文档配置AppGallery Connect 设置消息回执 集成华为厂商通道SDK…

带你了解二进制

目录 视频参考&#xff1a; 讲解&#xff1a;​编辑 运算&#xff1a; 1001&#xff08;二进制&#xff09; 9&#xff08;十位数&#xff09;1111&#xff08;二进制&#xff09; 15&#xff08;十位数&#xff09;11001&#xff08;二进制&#xff09; 25&#xff08;…

第二章 搭建TS环境

搭建 TypeScript 的开发环境。一个舒适、便捷且顺手的开发环境&#xff0c;不仅能大大提高学习效率&#xff0c;也会对我们日常的开发工作有很大帮助。 这一节我们就来介绍 VS Code 下的 TypeScript 环境搭建&#xff1a;插件以及配置项。对于 TS 文件的执行&#xff0c;我们会…

设计模式(十):结构型之外观模式

设计模式系列文章 设计模式(一)&#xff1a;创建型之单例模式 设计模式(二、三)&#xff1a;创建型之工厂方法和抽象工厂模式 设计模式(四)&#xff1a;创建型之原型模式 设计模式(五)&#xff1a;创建型之建造者模式 设计模式(六)&#xff1a;结构型之代理模式 设计模式…

备战2月面试8家大厂,成功上岸字节(Java岗)定级T2-2,分享一下我的面试心得

最近在公众号上看到一位道友的字节面经&#xff0c;可以说是深有感触了&#xff01; 他的背景是杭州某中厂的Java后端开发&#xff0c;本科毕业5年&#xff0c;最近2个月面试了PDD、小红书、字节等多个大厂。几乎都拿到了Offer&#xff0c;最终选择了字节2-2。以下是他的一些分…

Navicat对postgresql导入导出的坑

Navicat导出postgresql中的表&#xff0c;再新建数据库导入时通常会报错&#xff0c;往往是因为自增id导致的 可以看到&#xff0c;再次导入时会报错&#xff01; 解决办法如下&#xff1a; 解决办法&#xff1a; 重新导入&#xff0c;并执行以下命令&#xff1a;&#xff08…

Python从入门到精通_第0讲_Python的学习路线整理

写在最前&#xff1a; 为什么开这个专栏&#xff1a; 之前我做过一个专栏&#xff0c;专门介绍Python爬虫技术&#xff0c;这一专栏收获了很多朋友们的点赞收藏和关注。但是在爬虫技术专栏中&#xff0c;对于Python语言本身的讲解并不是很细致&#xff0c;由于Python在爬虫、数…

Unity 安装 wwise

先下载 https://www.audiokinetic.com/zh/download 安装的时候要选sdk 就是20g的那个 然后运行 选择unity 可以看到这个界面 好&#xff0c;现在开始要安装离线包 直接项目里点 第二个 装好后 他会提示你 无法找到unity安装的地址 1 打开你的 unity 编辑器 2 在unity安装的…

CorelDRAW矢量绘图2023中文版下载

市面上的矢量绘图工具虽然很多&#xff0c;但权威又专业的却不多&#xff0c;选到不好用的工具&#xff0c;会极大的影响自己创作&#xff0c;CorelDRAW简称cdr&#xff0c;是一款功能强大的矢量图制作软件&#xff0c;一说到矢量图制作&#xff0c;大家都会不由自主地想到cdr。…

车牌识别之UI(Tkinter + OpenCV显示Picture和Video)

画一张总图&#xff1a; 图形界面开发 本篇只介绍图形界面开发。 遇到的第一个问题就是选择什么开发语言和技术。因为我之前用Python做过Tkinter的小东西&#xff0c;所以这次还是用Python Tkinter OpenCV来搞吧。 这里面需要注意几个地方&#xff1a; 1. Tkinter 的布局 …

ADManager Plus:提升企业人员管理效率的全能工具

导言&#xff1a; 在现代企业中&#xff0c;高效的人员管理是取得成功的关键。随着企业规模的扩大和人员数量的增加&#xff0c;传统的人工管理方法已经无法满足快速变化的需求。ADManager Plus作为一款全能的人员管理工具&#xff0c;通过自动化和集成的方式&#xff0c;为企…

一文即可了解!自动化回归测试工具

目录 前言&#xff1a; 设计背景&#xff1a; 解决方案&#xff1a; 测试工具使用loadrunner脚本编写&#xff0c;这样的好处是 控制指令说明&#xff1a; 自定义检查方法说明&#xff1a; 变量说明&#xff1a; 附加动作说明&#xff1a; 前言&#xff1a; 自动化回归测试工…

Numpy---ndarray矩阵运算、广播机制、排序、文件操作

1. 矩阵运算 n 10 # 加 n - 10 # 减 n * 10 # 乘 n / 10 # 除 n // 2 # 整除 n ** 2 # 次方 n % 2 # 余数 n1 np.random.randint(0, 10, size(4, 5)) n2 np.random.randint(0, 10, size(4, 5)) display(n1, n2) array([[3, 6, 1, 9, 9],[8, 9, 2, 0, 4],[4, 8, 5, …

17 条件随机场

文章目录 17 条件随机场——CRF&#xff08;Condition Random Field&#xff09;17.1 背景介绍17.2 HMM与MEMM的区别17.3 MEMM与CRF的区别17.4 CRF模型17.4.1 CRF的概率密度函数17.4.2 CRF概率密度函数简化&#xff08;向量形式&#xff09; 17.5 CRF需要解决的问题17.6 边缘概…

基于 AIGC,RocketMQ 学习社区探索开源软件学习新范式

作者&#xff1a;寒斜 AIGC 持续火爆全球&#xff0c;越来越多的场景开始接入并体现非凡的价值。其中应用广泛的场景之一就是智能知识问答&#xff0c;它改变了人们学习的方式&#xff0c;从阅读式到问答式&#xff0c;让知识的获取更加精准有效。开源软件拥有着广泛的求知群体…

ITSM 如何帮助制造业企业

ITSM在现代制造业中的作用 在过去的几年中&#xff0c;制造业已经看到了快速的数字化&#xff0c;以智能制造技术改进生产技术。在工业4.0和工业5.0的推动下&#xff0c;制造商正在摆脱陈旧 以及利用物联网、人工智能、机器学习和大数据等先进技术的互联智能制造系统&#xff…

【Protobuf速成指南】Any类型的使用

文章目录 2.2 Any类型的使用一、基本认识二、使用需知三、Any字段的使用①修改proto文件② Any相关函数③ 类型转换 四、Contact 2.2 改写 2.2 Any类型的使用 本系列文章将通过对通讯录项目的不断完善&#xff0c;带大家由浅入深的学习Protobuf的使用。这是Contacts的2.2版本&a…

基于脑电功率的疲劳驾驶检测研究_kaic

基于脑电功率的疲劳驾驶检测研究 摘 要 在道路交通安全领域&#xff0c;疲劳驾驶是一种常见的交通安全隐患。现有数据统计&#xff0c;全球每年有大约&#xff12;&#xff11;&#xff05;的重大交通事故与疲劳驾驶有关&#xff0c;疲劳驾驶成为了诱发交通事故的主要原因之一…

多模态深度学习:定义、示例、应用

人类使用五种感官来体验和解读周围的世界。我们的五种感官从五个不同的来源和五种不同的方式捕捉信息。模态是指某事发生、经历或捕捉的方式。 人脑由可以同时处理多种模式的神经网络组成。想象一下进行对话——您大脑的神经网络处理多模式输入&#xff08;音频、视觉、文本、…

《树莓派4B家庭服务器搭建指南》第十六期:安装Calibre Web建立公网可访问私人电子书库

最近在整理收藏的电子书&#xff0c;以便在公网可以随时访问自己的电子书&#xff0c;然而&#xff0c;Calibre桌面端虽然功能强大&#xff0c;并且可以开启本地http服务&#xff0c;但http的界面还是过于简陋 我发现了一个Calibre Web开源项目&#xff0c;界面也非常舒服&…