import 'dart:math'; import 'dart:math' as math; import 'dart:ui' as ui; import 'dart:ui'; import 'package:flutter/material.dart'; class OnePage extends StatefulWidget { @override OnePageState createState() => new OnePageState(); } class OnePageState extends State { @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Color(0xFF3B62EE), appBar: AppBar( title: Text(''), ), body: Center( child: radarMapComponent( ["观察能力", "迁移发散能力", "归纳总结能力", "分类比较能力", "描述记录能力", "质疑猜想能力"], // ["观察能力", "迁移发散能力", "归纳总结能力", "分类比较能力",], 30.0, 18.0, context: context, ), ), ); } @override void initState() { // TODO: implement initState super.initState(); } } /// titlesList:外面的标题数组 /// minRadius:最小圆的半径 /// poorRadius:扩散圆的半径差 Widget radarMapComponent(List titlesList, double minRadius, double poorRadius, {BuildContext context}) { double maxRadius = minRadius + poorRadius * titlesList.length - 1; return Container( width: maxRadius*2 + 100, height: maxRadius*2 + 100, child: CustomPaint(size: Size(maxRadius*2 + 100, maxRadius*2 + 100), painter: TolyPainter(titlesList: titlesList, minRadius: minRadius, poorRadius: poorRadius, context: context)), ); } class TolyPainter extends CustomPainter { final BuildContext context; final List titlesList; final double minRadius; final double poorRadius; TolyPainter({@required this.context, this.titlesList, this.minRadius, this.poorRadius}); @override void paint(Canvas canvas, Size size) { double maxRadius = minRadius + poorRadius * (titlesList.length - 1); canvas.translate(size.width / 2, size.height / 2); //虚线画笔 Paint dottedLinePaint = Paint() ..style = PaintingStyle.stroke ..color = Color(0xFFB9BEE6) ..strokeWidth = 1; Paint paint = Paint() ..style = PaintingStyle.stroke ..color = Color(0xFFE5E3FD) ..strokeWidth = 1; //多边形画笔 Paint polygonBrushesPaint = Paint() ..style = PaintingStyle.fill ..color = Color(0xFFF6A5AB).withOpacity(0.52) ..strokeWidth = 1; Paint polygonBrushesPaint1 = Paint() ..style = PaintingStyle.stroke ..color = Color(0xFFF6A5AB) ..strokeWidth = 1; Path polygonBrushesPath = Path(); //文字画笔 TextPainter _textPainter = new TextPainter(textAlign: TextAlign.left, textDirection: TextDirection.ltr); final Path path = Path(); final dottedLinePath = Path(); //绘制文字 for (int i = 0; i < titlesList.length; i++) { Size textSize = boundingTextSize(context, titlesList[i], TextStyle(color: Colors.white, fontSize: 9)); canvas.save(); //与restore配合使用保存当前画布 canvas.translate(0.0, -maxRadius -10); //平移画布画点于时钟的12点位置,+30为了调整数字与刻度的间隔 _textPainter.text = TextSpan(style: TextStyle(color: Colors.white, fontSize: 9), text: titlesList[i]); canvas.rotate(-angleToRadian(360 / titlesList.length * i)); //保持画的文字竖直显示。 _textPainter.layout(); // double radiu = 360 / titlesList.length * i; if (radiu == 0 || radiu == 1) { _textPainter.paint(canvas, Offset(-_textPainter.width / 2 , -_textPainter.height / 2 - 5)); } else if(0 < radiu && radiu < 180){ _textPainter.paint(canvas, Offset(0, -5)); } else if(180 < radiu && radiu < 360){ _textPainter.paint(canvas, Offset(-_textPainter.width, -5)); } else{ _textPainter.paint(canvas, Offset(-_textPainter.width / 2, -_textPainter.height / 2 + 5)); } canvas.restore(); //画布重置,恢复到控件中心 canvas.rotate(angleToRadian(360 / titlesList.length)); //画布旋转一个区间刻度,把文字和刻度对应起来 } List pointsList = []; for (int i = 0; i < titlesList.length; i++) { path.moveTo(0, 0); dottedLinePath.moveTo(0, 0); if (i < titlesList.length - 1) { dottedLinePath.addOval(Rect.fromCircle(center: Offset.zero, radius: 30.0 + 18 * i)); } double maxRadius = minRadius + poorRadius * titlesList.length; double x = (30 + 18 * (titlesList.length - 1)) * cos(angleToRadian(360 / titlesList.length * (i + 1) - 90)); double y = (30 + 18 * (titlesList.length - 1)) * sin(angleToRadian(360 / titlesList.length * (i + 1) - 90)); path.lineTo(x, y); pointsList.add(Offset(cos(angleToRadian(360 / titlesList.length * (i + 1) - 90)) * maxRadius * (i + 1 > titlesList.length - 1 ? titlesList.length - 2 : i + 1) / titlesList.length, sin(angleToRadian(360 / titlesList.length * (i + 1) - 90)) * maxRadius * (i + 1 > titlesList.length - 1 ? titlesList.length - 1 : i + 1) / titlesList.length)); } polygonBrushesPath.addPolygon(pointsList, true); canvas.drawPath(polygonBrushesPath, polygonBrushesPaint); canvas.drawPath(polygonBrushesPath, polygonBrushesPaint1); for (int i = 0; i < titlesList.length; i++) { ParagraphBuilder pb = ParagraphBuilder(ParagraphStyle( textAlign: TextAlign.left, fontWeight: FontWeight.w600, fontStyle: FontStyle.normal, fontSize: 7, )) ..pushStyle(ui.TextStyle(color: Color(0xFFB9BEE6))) ..addText((i * 2).toString()); ParagraphConstraints pc = ParagraphConstraints(width: 50); Paragraph paragraph = pb.build()..layout(pc); canvas.drawParagraph(paragraph, Offset((minRadius + poorRadius * i) * (11 + 13 * i) / (minRadius+ poorRadius * i), (minRadius + poorRadius * i) * (11 + 13 * i) / (minRadius + poorRadius * i))); } DashPainter(span: 4, step: 9, pointWidth: 10).paint(canvas, dottedLinePath, dottedLinePaint); canvas.drawPath(path, paint); canvas.drawCircle(Offset.zero, minRadius + 18 * (titlesList.length - 1), paint); ParagraphBuilder pb = ParagraphBuilder(ParagraphStyle( textAlign: TextAlign.left, fontWeight: FontWeight.w600, fontStyle: FontStyle.normal, fontSize: 7, )) ..pushStyle(ui.TextStyle(color: Color(0xFFB9BEE6))) ..addText((titlesList.length * 2).toString()); ParagraphConstraints pc = ParagraphConstraints(width: 50); Paragraph paragraph = pb.build()..layout(pc); canvas.drawParagraph(paragraph, Offset((minRadius + poorRadius * titlesList.length) * (11 + 13 * titlesList.length) / (minRadius + poorRadius * titlesList.length), (minRadius + poorRadius * titlesList.length) * (11 + 13 * titlesList.length) / (minRadius + poorRadius * titlesList.length))); } static Size boundingTextSize(BuildContext context, String text, TextStyle style, {int maxLines = 2 ^ 31, double maxWidth = double.infinity}) { if (text == null || text.isEmpty) { return Size.zero; } final TextPainter textPainter = TextPainter(textDirection: TextDirection.ltr, locale: Localizations.maybeLocaleOf(context), text: TextSpan(text: text, style: style), maxLines: maxLines)..layout(maxWidth: maxWidth); return textPainter.size; } ///角度换算成弧度 参数[angle]为角度 static double angleToRadian(double angle) { return angle * pi / 180; } @override bool shouldRepaint(covariant TolyPainter oldDelegate) => false; } class DashPainter { const DashPainter({ this.step = 2, this.span = 2, this.pointCount = 0, this.pointWidth, }); final double step; //实现的长度 final double span; //虚线的长度 final int pointCount; //点划线数 final double pointWidth; //点划线长 void paint(Canvas canvas, Path path, Paint paint) { final PathMetrics pms = path.computeMetrics(); //为此路径创建一个PathMetrics对象,该对象可以描述有关路径轮廓的各种属性。 final double pointLineLength = pointWidth; final double partLength = step + span * (pointCount + 1) + pointCount * pointLineLength; pms.forEach((PathMetric pm) { final int count = pm.length ~/ partLength; for (int i = 0; i < count; i++) { canvas.drawPath( pm.extractPath(partLength * i, partLength * i + step), paint, ); for (int j = 1; j <= pointCount; j++) { final start = partLength * i + step + span * j + pointLineLength * (j - 1); canvas.drawPath( pm.extractPath(start, start + pointLineLength), paint, ); } } final double tail = pm.length % partLength; canvas.drawPath(pm.extractPath(pm.length - tail, pm.length), paint); }); } }