flutter 雷达图

通过CustomPainter自定义雷达图

效果如下

主要代码

Dart 复制代码
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 雷达图

相关推荐
江上清风山间明月1 天前
Flutter开发的应用页面非常多时如何高效管理路由
android·flutter·路由·页面管理·routes·ongenerateroute
Zsnoin能2 天前
flutter国际化、主题配置、视频播放器UI、扫码功能、水波纹问题
flutter
早起的年轻人2 天前
Flutter CupertinoNavigationBar iOS 风格导航栏的组件
flutter·ios
HappyAcmen2 天前
关于Flutter前端面试题及其答案解析
前端·flutter
coooliang2 天前
Flutter 中的单例模式
javascript·flutter·单例模式
coooliang2 天前
Flutter项目中设置安卓启动页
android·flutter
JIngles1232 天前
flutter将utf-8编码的字节序列转换为中英文字符串
java·javascript·flutter
B.-2 天前
在 Flutter 中实现文件读写
开发语言·学习·flutter·android studio·xcode
freflying11192 天前
使用jenkins构建Android+Flutter项目依赖自动升级带来兼容性问题及Jenkins构建速度慢问题解决
android·flutter·jenkins
机器瓦力3 天前
Flutter应用开发:对象存储管理图片
flutter