one_page.dart 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. import 'dart:math';
  2. import 'dart:math' as math;
  3. import 'dart:ui' as ui;
  4. import 'dart:ui';
  5. import 'package:flutter/material.dart';
  6. class OnePage extends StatefulWidget {
  7. @override
  8. OnePageState createState() => new OnePageState();
  9. }
  10. class OnePageState extends State<OnePage> {
  11. @override
  12. Widget build(BuildContext context) {
  13. return Scaffold(
  14. backgroundColor: Color(0xFF3B62EE),
  15. appBar: AppBar(
  16. title: Text(''),
  17. ),
  18. body: Center(
  19. child: radarMapComponent(
  20. ["观察能力", "迁移发散能力", "归纳总结能力", "分类比较能力", "描述记录能力", "质疑猜想能力"],
  21. // ["观察能力", "迁移发散能力", "归纳总结能力", "分类比较能力",],
  22. 30.0,
  23. 18.0,
  24. context: context,
  25. ),
  26. ),
  27. );
  28. }
  29. @override
  30. void initState() {
  31. // TODO: implement initState
  32. super.initState();
  33. }
  34. }
  35. /// titlesList:外面的标题数组
  36. /// minRadius:最小圆的半径
  37. /// poorRadius:扩散圆的半径差
  38. Widget radarMapComponent(List titlesList, double minRadius, double poorRadius, {BuildContext context}) {
  39. double maxRadius = minRadius + poorRadius * titlesList.length - 1;
  40. return Container(
  41. width: maxRadius*2 + 100,
  42. height: maxRadius*2 + 100,
  43. child: CustomPaint(size: Size(maxRadius*2 + 100, maxRadius*2 + 100), painter: TolyPainter(titlesList: titlesList, minRadius: minRadius, poorRadius: poorRadius, context: context)),
  44. );
  45. }
  46. class TolyPainter extends CustomPainter {
  47. final BuildContext context;
  48. final List titlesList;
  49. final double minRadius;
  50. final double poorRadius;
  51. TolyPainter({@required this.context, this.titlesList, this.minRadius, this.poorRadius});
  52. @override
  53. void paint(Canvas canvas, Size size) {
  54. double maxRadius = minRadius + poorRadius * (titlesList.length - 1);
  55. canvas.translate(size.width / 2, size.height / 2);
  56. //虚线画笔
  57. Paint dottedLinePaint = Paint()
  58. ..style = PaintingStyle.stroke
  59. ..color = Color(0xFFB9BEE6)
  60. ..strokeWidth = 1;
  61. Paint paint = Paint()
  62. ..style = PaintingStyle.stroke
  63. ..color = Color(0xFFE5E3FD)
  64. ..strokeWidth = 1;
  65. //多边形画笔
  66. Paint polygonBrushesPaint = Paint()
  67. ..style = PaintingStyle.fill
  68. ..color = Color(0xFFF6A5AB).withOpacity(0.52)
  69. ..strokeWidth = 1;
  70. Paint polygonBrushesPaint1 = Paint()
  71. ..style = PaintingStyle.stroke
  72. ..color = Color(0xFFF6A5AB)
  73. ..strokeWidth = 1;
  74. Path polygonBrushesPath = Path();
  75. //文字画笔
  76. TextPainter _textPainter = new TextPainter(textAlign: TextAlign.left, textDirection: TextDirection.ltr);
  77. final Path path = Path();
  78. final dottedLinePath = Path();
  79. //绘制文字
  80. for (int i = 0; i < titlesList.length; i++) {
  81. Size textSize = boundingTextSize(context, titlesList[i], TextStyle(color: Colors.white, fontSize: 9));
  82. canvas.save(); //与restore配合使用保存当前画布
  83. canvas.translate(0.0, -maxRadius -10); //平移画布画点于时钟的12点位置,+30为了调整数字与刻度的间隔
  84. _textPainter.text = TextSpan(style: TextStyle(color: Colors.white, fontSize: 9), text: titlesList[i]);
  85. canvas.rotate(-angleToRadian(360 / titlesList.length * i)); //保持画的文字竖直显示。
  86. _textPainter.layout();
  87. //
  88. double radiu = 360 / titlesList.length * i;
  89. if (radiu == 0 || radiu == 1) {
  90. _textPainter.paint(canvas, Offset(-_textPainter.width / 2 , -_textPainter.height / 2 - 5));
  91. }
  92. else if(0 < radiu && radiu < 180){
  93. _textPainter.paint(canvas, Offset(0, -5));
  94. }
  95. else if(180 < radiu && radiu < 360){
  96. _textPainter.paint(canvas, Offset(-_textPainter.width, -5));
  97. }
  98. else{
  99. _textPainter.paint(canvas, Offset(-_textPainter.width / 2, -_textPainter.height / 2 + 5));
  100. }
  101. canvas.restore(); //画布重置,恢复到控件中心
  102. canvas.rotate(angleToRadian(360 / titlesList.length)); //画布旋转一个区间刻度,把文字和刻度对应起来
  103. }
  104. List<Offset> pointsList = [];
  105. for (int i = 0; i < titlesList.length; i++) {
  106. path.moveTo(0, 0);
  107. dottedLinePath.moveTo(0, 0);
  108. if (i < titlesList.length - 1) {
  109. dottedLinePath.addOval(Rect.fromCircle(center: Offset.zero, radius: 30.0 + 18 * i));
  110. }
  111. double maxRadius = minRadius + poorRadius * titlesList.length;
  112. double x = (30 + 18 * (titlesList.length - 1)) * cos(angleToRadian(360 / titlesList.length * (i + 1) - 90));
  113. double y = (30 + 18 * (titlesList.length - 1)) * sin(angleToRadian(360 / titlesList.length * (i + 1) - 90));
  114. path.lineTo(x, y);
  115. 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));
  116. }
  117. polygonBrushesPath.addPolygon(pointsList, true);
  118. canvas.drawPath(polygonBrushesPath, polygonBrushesPaint);
  119. canvas.drawPath(polygonBrushesPath, polygonBrushesPaint1);
  120. for (int i = 0; i < titlesList.length; i++) {
  121. ParagraphBuilder pb = ParagraphBuilder(ParagraphStyle(
  122. textAlign: TextAlign.left,
  123. fontWeight: FontWeight.w600,
  124. fontStyle: FontStyle.normal,
  125. fontSize: 7,
  126. ))
  127. ..pushStyle(ui.TextStyle(color: Color(0xFFB9BEE6)))
  128. ..addText((i * 2).toString());
  129. ParagraphConstraints pc = ParagraphConstraints(width: 50);
  130. Paragraph paragraph = pb.build()..layout(pc);
  131. canvas.drawParagraph(paragraph, Offset((minRadius + poorRadius * i) * (11 + 13 * i) / (minRadius+ poorRadius * i), (minRadius + poorRadius * i) * (11 + 13 * i) / (minRadius + poorRadius * i)));
  132. }
  133. DashPainter(span: 4, step: 9, pointWidth: 10).paint(canvas, dottedLinePath, dottedLinePaint);
  134. canvas.drawPath(path, paint);
  135. canvas.drawCircle(Offset.zero, minRadius + 18 * (titlesList.length - 1), paint);
  136. ParagraphBuilder pb = ParagraphBuilder(ParagraphStyle(
  137. textAlign: TextAlign.left,
  138. fontWeight: FontWeight.w600,
  139. fontStyle: FontStyle.normal,
  140. fontSize: 7,
  141. ))
  142. ..pushStyle(ui.TextStyle(color: Color(0xFFB9BEE6)))
  143. ..addText((titlesList.length * 2).toString());
  144. ParagraphConstraints pc = ParagraphConstraints(width: 50);
  145. Paragraph paragraph = pb.build()..layout(pc);
  146. 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)));
  147. }
  148. static Size boundingTextSize(BuildContext context, String text, TextStyle style, {int maxLines = 2 ^ 31, double maxWidth = double.infinity}) {
  149. if (text == null || text.isEmpty) {
  150. return Size.zero;
  151. }
  152. final TextPainter textPainter = TextPainter(textDirection: TextDirection.ltr, locale: Localizations.maybeLocaleOf(context), text: TextSpan(text: text, style: style), maxLines: maxLines)..layout(maxWidth: maxWidth);
  153. return textPainter.size;
  154. }
  155. ///角度换算成弧度 参数[angle]为角度
  156. static double angleToRadian(double angle) {
  157. return angle * pi / 180;
  158. }
  159. @override
  160. bool shouldRepaint(covariant TolyPainter oldDelegate) => false;
  161. }
  162. class DashPainter {
  163. const DashPainter({
  164. this.step = 2,
  165. this.span = 2,
  166. this.pointCount = 0,
  167. this.pointWidth,
  168. });
  169. final double step; //实现的长度
  170. final double span; //虚线的长度
  171. final int pointCount; //点划线数
  172. final double pointWidth; //点划线长
  173. void paint(Canvas canvas, Path path, Paint paint) {
  174. final PathMetrics pms = path.computeMetrics(); //为此路径创建一个PathMetrics对象,该对象可以描述有关路径轮廓的各种属性。
  175. final double pointLineLength = pointWidth;
  176. final double partLength = step + span * (pointCount + 1) + pointCount * pointLineLength;
  177. pms.forEach((PathMetric pm) {
  178. final int count = pm.length ~/ partLength;
  179. for (int i = 0; i < count; i++) {
  180. canvas.drawPath(
  181. pm.extractPath(partLength * i, partLength * i + step),
  182. paint,
  183. );
  184. for (int j = 1; j <= pointCount; j++) {
  185. final start = partLength * i + step + span * j + pointLineLength * (j - 1);
  186. canvas.drawPath(
  187. pm.extractPath(start, start + pointLineLength),
  188. paint,
  189. );
  190. }
  191. }
  192. final double tail = pm.length % partLength;
  193. canvas.drawPath(pm.extractPath(pm.length - tail, pm.length), paint);
  194. });
  195. }
  196. }