one_page.dart 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  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(
  39. List titlesList,
  40. double minRadius,
  41. double poorRadius, {
  42. required BuildContext context,
  43. }) {
  44. double maxRadius = minRadius + poorRadius * titlesList.length - 1;
  45. return Container(
  46. width: maxRadius * 2 + 100,
  47. height: maxRadius * 2 + 100,
  48. child: CustomPaint(
  49. size: Size(maxRadius * 2 + 100, maxRadius * 2 + 100),
  50. painter: TolyPainter(
  51. titlesList: titlesList,
  52. minRadius: minRadius,
  53. poorRadius: poorRadius,
  54. context: context)),
  55. );
  56. }
  57. class TolyPainter extends CustomPainter {
  58. final BuildContext context;
  59. final List titlesList;
  60. final double minRadius;
  61. final double poorRadius;
  62. TolyPainter({
  63. required this.context,
  64. required this.titlesList,
  65. required this.minRadius,
  66. required this.poorRadius,
  67. });
  68. @override
  69. void paint(Canvas canvas, Size size) {
  70. double maxRadius = minRadius + poorRadius * (titlesList.length - 1);
  71. canvas.translate(size.width / 2, size.height / 2);
  72. //虚线画笔
  73. Paint dottedLinePaint = Paint()
  74. ..style = PaintingStyle.stroke
  75. ..color = Color(0xFFB9BEE6)
  76. ..strokeWidth = 1;
  77. Paint paint = Paint()
  78. ..style = PaintingStyle.stroke
  79. ..color = Color(0xFFE5E3FD)
  80. ..strokeWidth = 1;
  81. //多边形画笔
  82. Paint polygonBrushesPaint = Paint()
  83. ..style = PaintingStyle.fill
  84. ..color = Color(0xFFF6A5AB).withOpacity(0.52)
  85. ..strokeWidth = 1;
  86. Paint polygonBrushesPaint1 = Paint()
  87. ..style = PaintingStyle.stroke
  88. ..color = Color(0xFFF6A5AB)
  89. ..strokeWidth = 1;
  90. Path polygonBrushesPath = Path();
  91. //文字画笔
  92. TextPainter _textPainter = new TextPainter(
  93. textAlign: TextAlign.left, textDirection: TextDirection.ltr);
  94. final Path path = Path();
  95. final dottedLinePath = Path();
  96. //绘制文字
  97. for (int i = 0; i < titlesList.length; i++) {
  98. Size textSize = boundingTextSize(
  99. context, titlesList[i], TextStyle(color: Colors.white, fontSize: 9));
  100. canvas.save(); //与restore配合使用保存当前画布
  101. canvas.translate(0.0, -maxRadius - 10); //平移画布画点于时钟的12点位置,+30为了调整数字与刻度的间隔
  102. _textPainter.text = TextSpan(
  103. style: TextStyle(color: Colors.white, fontSize: 9),
  104. text: titlesList[i]);
  105. canvas.rotate(-angleToRadian(360 / titlesList.length * i)); //保持画的文字竖直显示。
  106. _textPainter.layout();
  107. //
  108. double radiu = 360 / titlesList.length * i;
  109. if (radiu == 0 || radiu == 1) {
  110. _textPainter.paint(canvas,
  111. Offset(-_textPainter.width / 2, -_textPainter.height / 2 - 5));
  112. } else if (0 < radiu && radiu < 180) {
  113. _textPainter.paint(canvas, Offset(0, -5));
  114. } else if (180 < radiu && radiu < 360) {
  115. _textPainter.paint(canvas, Offset(-_textPainter.width, -5));
  116. } else {
  117. _textPainter.paint(canvas,
  118. Offset(-_textPainter.width / 2, -_textPainter.height / 2 + 5));
  119. }
  120. canvas.restore(); //画布重置,恢复到控件中心
  121. canvas.rotate(
  122. angleToRadian(360 / titlesList.length)); //画布旋转一个区间刻度,把文字和刻度对应起来
  123. }
  124. List<Offset> pointsList = [];
  125. for (int i = 0; i < titlesList.length; i++) {
  126. path.moveTo(0, 0);
  127. dottedLinePath.moveTo(0, 0);
  128. if (i < titlesList.length - 1) {
  129. dottedLinePath.addOval(
  130. Rect.fromCircle(center: Offset.zero, radius: 30.0 + 18 * i));
  131. }
  132. double maxRadius = minRadius + poorRadius * titlesList.length;
  133. double x = (30 + 18 * (titlesList.length - 1)) *
  134. cos(angleToRadian(360 / titlesList.length * (i + 1) - 90));
  135. double y = (30 + 18 * (titlesList.length - 1)) *
  136. sin(angleToRadian(360 / titlesList.length * (i + 1) - 90));
  137. path.lineTo(x, y);
  138. pointsList.add(Offset(
  139. cos(angleToRadian(360 / titlesList.length * (i + 1) - 90)) *
  140. maxRadius *
  141. (i + 1 > titlesList.length - 1 ? titlesList.length - 2 : i + 1) /
  142. titlesList.length,
  143. sin(angleToRadian(360 / titlesList.length * (i + 1) - 90)) *
  144. maxRadius *
  145. (i + 1 > titlesList.length - 1 ? titlesList.length - 1 : i + 1) /
  146. titlesList.length));
  147. }
  148. polygonBrushesPath.addPolygon(pointsList, true);
  149. canvas.drawPath(polygonBrushesPath, polygonBrushesPaint);
  150. canvas.drawPath(polygonBrushesPath, polygonBrushesPaint1);
  151. for (int i = 0; i < titlesList.length; i++) {
  152. ParagraphBuilder pb = ParagraphBuilder(ParagraphStyle(
  153. textAlign: TextAlign.left,
  154. fontWeight: FontWeight.w600,
  155. fontStyle: FontStyle.normal,
  156. fontSize: 7,
  157. ))
  158. ..pushStyle(ui.TextStyle(color: Color(0xFFB9BEE6)))
  159. ..addText((i * 2).toString());
  160. ParagraphConstraints pc = ParagraphConstraints(width: 50);
  161. Paragraph paragraph = pb.build()..layout(pc);
  162. canvas.drawParagraph(
  163. paragraph,
  164. Offset(
  165. (minRadius + poorRadius * i) *
  166. (11 + 13 * i) /
  167. (minRadius + poorRadius * i),
  168. (minRadius + poorRadius * i) *
  169. (11 + 13 * i) /
  170. (minRadius + poorRadius * i)));
  171. }
  172. DashPainter(span: 4, step: 9, pointWidth: 10)
  173. .paint(canvas, dottedLinePath, dottedLinePaint);
  174. canvas.drawPath(path, paint);
  175. canvas.drawCircle(
  176. Offset.zero, minRadius + 18 * (titlesList.length - 1), paint);
  177. ParagraphBuilder pb = ParagraphBuilder(ParagraphStyle(
  178. textAlign: TextAlign.left,
  179. fontWeight: FontWeight.w600,
  180. fontStyle: FontStyle.normal,
  181. fontSize: 7,
  182. ))
  183. ..pushStyle(ui.TextStyle(color: Color(0xFFB9BEE6)))
  184. ..addText((titlesList.length * 2).toString());
  185. ParagraphConstraints pc = ParagraphConstraints(width: 50);
  186. Paragraph paragraph = pb.build()..layout(pc);
  187. canvas.drawParagraph(
  188. paragraph,
  189. Offset(
  190. (minRadius + poorRadius * titlesList.length) *
  191. (11 + 13 * titlesList.length) /
  192. (minRadius + poorRadius * titlesList.length),
  193. (minRadius + poorRadius * titlesList.length) *
  194. (11 + 13 * titlesList.length) /
  195. (minRadius + poorRadius * titlesList.length)));
  196. }
  197. static Size boundingTextSize(
  198. BuildContext context, String text, TextStyle style,
  199. {int maxLines = 2 ^ 31, double maxWidth = double.infinity}) {
  200. if (text == null || text.isEmpty) {
  201. return Size.zero;
  202. }
  203. final TextPainter textPainter = TextPainter(
  204. textDirection: TextDirection.ltr,
  205. locale: Localizations.maybeLocaleOf(context),
  206. text: TextSpan(text: text, style: style),
  207. maxLines: maxLines)
  208. ..layout(maxWidth: maxWidth);
  209. return textPainter.size;
  210. }
  211. ///角度换算成弧度 参数[angle]为角度
  212. static double angleToRadian(double angle) {
  213. return angle * pi / 180;
  214. }
  215. @override
  216. bool shouldRepaint(covariant TolyPainter oldDelegate) => false;
  217. }
  218. class DashPainter {
  219. const DashPainter({
  220. this.step = 2,
  221. this.span = 2,
  222. this.pointCount = 0,
  223. required this.pointWidth,
  224. });
  225. final double step; //实现的长度
  226. final double span; //虚线的长度
  227. final int pointCount; //点划线数
  228. final double pointWidth; //点划线长
  229. void paint(Canvas canvas, Path path, Paint paint) {
  230. final PathMetrics pms =
  231. path.computeMetrics(); //为此路径创建一个PathMetrics对象,该对象可以描述有关路径轮廓的各种属性。
  232. final double pointLineLength = pointWidth;
  233. final double partLength =
  234. step + span * (pointCount + 1) + pointCount * pointLineLength;
  235. pms.forEach((PathMetric pm) {
  236. final int count = pm.length ~/ partLength;
  237. for (int i = 0; i < count; i++) {
  238. canvas.drawPath(
  239. pm.extractPath(partLength * i, partLength * i + step),
  240. paint,
  241. );
  242. for (int j = 1; j <= pointCount; j++) {
  243. final start =
  244. partLength * i + step + span * j + pointLineLength * (j - 1);
  245. canvas.drawPath(
  246. pm.extractPath(start, start + pointLineLength),
  247. paint,
  248. );
  249. }
  250. }
  251. final double tail = pm.length % partLength;
  252. canvas.drawPath(pm.extractPath(pm.length - tail, pm.length), paint);
  253. });
  254. }
  255. }