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 雷达图

相关推荐
bu_xue9 小时前
【LiveStates 01】别再手动 watch 了:开启 Flutter “自动追踪” DX 革命
flutter
bu_xue9 小时前
【LiveStates 05】实战指南:手把手带你用 LiveStates 构建高性能生产级页面
flutter
程序员老刘10 小时前
AI写Flutter代码比我快100倍,我慌了吗?
flutter·ai编程·客户端
SY.ZHOU13 小时前
大型工程跨全平台实践总结
flutter·ios·安卓·鸿蒙
weixin_4434785116 小时前
Flutter学习之导航与路由
java·学习·flutter
恋猫de小郭17 小时前
Flutter 鸿蒙 2026 路线发布,加速同步官方生态,进一步优化体验
前端·flutter·harmonyos
亚历克斯神17 小时前
Flutter 三方库 fft 的鸿蒙化适配指南 - 实现端侧高性能快速傅里叶变换、支持音频频谱分析与信号处理域的频域特征提取实战
flutter·harmonyos·鸿蒙·openharmony
鹏多多18 小时前
Flutter使用pretty_qr_code生成高颜值二维码
android·前端·flutter
不爱吃糖的程序媛2 天前
Flutter 3.35.7-ohos-0.0.3 发布:能力增强、性能优化与多项问题修复
flutter
始持2 天前
第三讲 进阶布局与样式(精细化UI)
flutter