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