通过CustomPainter进行绘制,可实现一些复杂的自定义绘制组件,是Flutter中自定义组件的灵魂人物。

CustomPaint绘线图形

【painter】 : 绘画器 【CustomPainter】
image.png

  1. import 'package:flutter/material.dart';
  2. class ClockPage extends StatelessWidget {
  3. @override
  4. Widget build(BuildContext context) {
  5. return Container(
  6. width: MediaQuery.of(context).size.width,
  7. height: 100,
  8. child:CustomPaint(//使用CustomPaint盛放画布
  9. painter: ClockPainter(),
  10. ),
  11. )
  12. ;
  13. }
  14. }
  15. class ClockPainter extends CustomPainter {
  16. Paint _paint;
  17. var _radius = 3.0; //小球半径
  18. Path _path = Path(); //画笔对象
  19. ClockPainter () {
  20. _paint = Paint()..color= Color(0xff45d0fd)..isAntiAlias=true;
  21. _path.addOval(Rect.fromCircle(radius: _radius, center: Offset(0, 0))); //小球路径
  22. }
  23. @override
  24. void paint(Canvas canvas, Size size) {
  25. print(size);
  26. canvas.clipRect(Offset.zero & size);
  27. canvas.translate(size.width/2-65*2, 0);
  28. renderDigit(1, canvas);//渲染数字
  29. canvas.translate(65, 0);//平移画布
  30. renderDigit(9, canvas);
  31. canvas.translate(65, 0); renderDigit(9, canvas);
  32. canvas.translate(65, 0); renderDigit(4, canvas);
  33. }
  34. //渲染数字 num :要显示的数字 canvas :画布
  35. void renderDigit(int num, Canvas canvas) {
  36. if (num > 10) { return; }
  37. for (int i = 0; i < digit[num].length; i++) {
  38. for (int j = 0; j < digit[num][j].length; j++) {
  39. if (digit[num][i][j] == 1) {
  40. canvas.save();
  41. double rX = j * 2 * (_radius + 1) + (_radius + 1); //第(i,j)个点圆心横坐标
  42. double rY = i * 2 * (_radius + 1) + (_radius + 1); //第(i,j)个点圆心纵坐标
  43. canvas.translate(rX, rY);
  44. canvas.drawPath(_path, _paint);
  45. canvas.restore();
  46. }
  47. }
  48. }
  49. }
  50. @override
  51. bool shouldRepaint(CustomPainter oldDelegate)=> false;
  52. }
  53. const digit = [
  54. [
  55. [0, 0, 1, 1, 1, 0, 0],
  56. [0, 1, 1, 0, 1, 1, 0],
  57. [1, 1, 0, 0, 0, 1, 1],
  58. [1, 1, 0, 0, 0, 1, 1],
  59. [1, 1, 0, 0, 0, 1, 1],
  60. [1, 1, 0, 0, 0, 1, 1],
  61. [1, 1, 0, 0, 0, 1, 1],
  62. [1, 1, 0, 0, 0, 1, 1],
  63. [0, 1, 1, 0, 1, 1, 0],
  64. [0, 0, 1, 1, 1, 0, 0]
  65. ], //0
  66. [
  67. [0, 0, 0, 1, 1, 0, 0],
  68. [0, 1, 1, 1, 1, 0, 0],
  69. [0, 0, 0, 1, 1, 0, 0],
  70. [0, 0, 0, 1, 1, 0, 0],
  71. [0, 0, 0, 1, 1, 0, 0],
  72. [0, 0, 0, 1, 1, 0, 0],
  73. [0, 0, 0, 1, 1, 0, 0],
  74. [0, 0, 0, 1, 1, 0, 0],
  75. [0, 0, 0, 1, 1, 0, 0],
  76. [1, 1, 1, 1, 1, 1, 1]
  77. ], //1
  78. [
  79. [0, 1, 1, 1, 1, 1, 0],
  80. [1, 1, 0, 0, 0, 1, 1],
  81. [0, 0, 0, 0, 0, 1, 1],
  82. [0, 0, 0, 0, 1, 1, 0],
  83. [0, 0, 0, 1, 1, 0, 0],
  84. [0, 0, 1, 1, 0, 0, 0],
  85. [0, 1, 1, 0, 0, 0, 0],
  86. [1, 1, 0, 0, 0, 0, 0],
  87. [1, 1, 0, 0, 0, 1, 1],
  88. [1, 1, 1, 1, 1, 1, 1]
  89. ], //2
  90. [
  91. [1, 1, 1, 1, 1, 1, 1],
  92. [0, 0, 0, 0, 0, 1, 1],
  93. [0, 0, 0, 0, 1, 1, 0],
  94. [0, 0, 0, 1, 1, 0, 0],
  95. [0, 0, 1, 1, 1, 0, 0],
  96. [0, 0, 0, 0, 1, 1, 0],
  97. [0, 0, 0, 0, 0, 1, 1],
  98. [0, 0, 0, 0, 0, 1, 1],
  99. [1, 1, 0, 0, 0, 1, 1],
  100. [0, 1, 1, 1, 1, 1, 0]
  101. ], //3
  102. [
  103. [0, 0, 0, 0, 1, 1, 0],
  104. [0, 0, 0, 1, 1, 1, 0],
  105. [0, 0, 1, 1, 1, 1, 0],
  106. [0, 1, 1, 0, 1, 1, 0],
  107. [1, 1, 0, 0, 1, 1, 0],
  108. [1, 1, 1, 1, 1, 1, 1],
  109. [0, 0, 0, 0, 1, 1, 0],
  110. [0, 0, 0, 0, 1, 1, 0],
  111. [0, 0, 0, 0, 1, 1, 0],
  112. [0, 0, 0, 1, 1, 1, 1]
  113. ], //4
  114. [
  115. [1, 1, 1, 1, 1, 1, 1],
  116. [1, 1, 0, 0, 0, 0, 0],
  117. [1, 1, 0, 0, 0, 0, 0],
  118. [1, 1, 1, 1, 1, 1, 0],
  119. [0, 0, 0, 0, 0, 1, 1],
  120. [0, 0, 0, 0, 0, 1, 1],
  121. [0, 0, 0, 0, 0, 1, 1],
  122. [0, 0, 0, 0, 0, 1, 1],
  123. [1, 1, 0, 0, 0, 1, 1],
  124. [0, 1, 1, 1, 1, 1, 0]
  125. ], //5
  126. [
  127. [0, 0, 0, 0, 1, 1, 0],
  128. [0, 0, 1, 1, 0, 0, 0],
  129. [0, 1, 1, 0, 0, 0, 0],
  130. [1, 1, 0, 0, 0, 0, 0],
  131. [1, 1, 0, 1, 1, 1, 0],
  132. [1, 1, 0, 0, 0, 1, 1],
  133. [1, 1, 0, 0, 0, 1, 1],
  134. [1, 1, 0, 0, 0, 1, 1],
  135. [1, 1, 0, 0, 0, 1, 1],
  136. [0, 1, 1, 1, 1, 1, 0]
  137. ], //6
  138. [
  139. [1, 1, 1, 1, 1, 1, 1],
  140. [1, 1, 0, 0, 0, 1, 1],
  141. [0, 0, 0, 0, 1, 1, 0],
  142. [0, 0, 0, 0, 1, 1, 0],
  143. [0, 0, 0, 1, 1, 0, 0],
  144. [0, 0, 0, 1, 1, 0, 0],
  145. [0, 0, 1, 1, 0, 0, 0],
  146. [0, 0, 1, 1, 0, 0, 0],
  147. [0, 0, 1, 1, 0, 0, 0],
  148. [0, 0, 1, 1, 0, 0, 0]
  149. ], //7
  150. [
  151. [0, 1, 1, 1, 1, 1, 0],
  152. [1, 1, 0, 0, 0, 1, 1],
  153. [1, 1, 0, 0, 0, 1, 1],
  154. [1, 1, 0, 0, 0, 1, 1],
  155. [0, 1, 1, 1, 1, 1, 0],
  156. [1, 1, 0, 0, 0, 1, 1],
  157. [1, 1, 0, 0, 0, 1, 1],
  158. [1, 1, 0, 0, 0, 1, 1],
  159. [1, 1, 0, 0, 0, 1, 1],
  160. [0, 1, 1, 1, 1, 1, 0]
  161. ], //8
  162. [
  163. [0, 1, 1, 1, 1, 1, 0],
  164. [1, 1, 0, 0, 0, 1, 1],
  165. [1, 1, 0, 0, 0, 1, 1],
  166. [1, 1, 0, 0, 0, 1, 1],
  167. [0, 1, 1, 1, 0, 1, 1],
  168. [0, 0, 0, 0, 0, 1, 1],
  169. [0, 0, 0, 0, 0, 1, 1],
  170. [0, 0, 0, 0, 1, 1, 0],
  171. [0, 0, 0, 1, 1, 0, 0],
  172. [0, 1, 1, 0, 0, 0, 0]
  173. ], //9
  174. [
  175. [0, 0, 0, 0],
  176. [0, 0, 0, 0],
  177. [0, 1, 1, 0],
  178. [0, 1, 1, 0],
  179. [0, 0, 0, 0],
  180. [0, 0, 0, 0],
  181. [0, 1, 1, 0],
  182. [0, 1, 1, 0],
  183. [0, 0, 0, 0],
  184. [0, 0, 0, 0]
  185. ] //:
  186. ];

CustomPaint绘线贝塞尔曲线

Flutter也支持贝塞尔曲线等复杂绘制。
image.png

import 'dart:ui';
import 'package:flutter/material.dart';
class PlayBezier3Page extends StatefulWidget {
  @override
  _PlayBezier3PageState createState() => _PlayBezier3PageState();
}

class _PlayBezier3PageState extends State<PlayBezier3Page> {
  List<Offset> _pos = <Offset>[];
  int selectPos;

  @override
  void initState() {
    _initPoints();
    super.initState();
  }

  void _initPoints() {
    _pos = List<Offset>();
    _pos.add(Offset(0, 0));
    _pos.add(Offset(60, -60));
    _pos.add(Offset(-90, -90));
    _pos.add(Offset(-120, -40));
  }


  @override
  Widget build(BuildContext context) {
    return  Container(
        height: 200,
        width: MediaQuery.of(context).size.width,
        child: CustomPaint(
          painter: BezierPainter(pos: _pos, selectPos: selectPos),
        ),

    );
  }
}

class BezierPainter extends CustomPainter {
  Paint _gridPaint;
  Path _gridPath;

  Paint _mainPaint;
  Path _mainPath;
  int selectPos;
  Paint _helpPaint;

  List<Offset> pos;

  BezierPainter({this.pos, this.selectPos}) {
    _gridPaint = Paint()..style = PaintingStyle.stroke;
    _gridPath = Path();

    _mainPaint = Paint()
      ..color = Colors.orange
      ..style = PaintingStyle.stroke
      ..strokeWidth = 2;
    _mainPath = Path();

    _helpPaint = Paint()
      ..color = Colors.purple
      ..style = PaintingStyle.stroke
      ..strokeWidth = 2
      ..strokeCap = StrokeCap.round;
  }

  @override
  void paint(Canvas canvas, Size size) {
    canvas.clipRect(Offset.zero & size);
    canvas.translate(size.width / 2, size.height / 2);
    _drawGrid(canvas, size); //绘制格线
    _drawAxis(canvas, size); //绘制轴线

      _mainPath.moveTo(pos[0].dx, pos[0].dy);
      _mainPath.cubicTo(pos[1].dx, pos[1].dy, pos[2].dx, pos[2].dy, pos[3].dx, pos[3].dy);
      canvas.drawPath(_mainPath, _mainPaint);
      _drawHelp(canvas);
      _drawSelectPos(canvas);

  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => false;

  void _drawGrid(Canvas canvas, Size size) {
    _gridPaint
      ..color = Colors.grey
      ..strokeWidth = 0.5;
    _gridPath = _buildGridPath(_gridPath, size);
    canvas.drawPath(_buildGridPath(_gridPath, size), _gridPaint);

    canvas.save();
    canvas.scale(1, -1); //沿x轴镜像
    canvas.drawPath(_gridPath, _gridPaint);
    canvas.restore();

    canvas.save();
    canvas.scale(-1, 1); //沿y轴镜像
    canvas.drawPath(_gridPath, _gridPaint);
    canvas.restore();

    canvas.save();
    canvas.scale(-1, -1); //沿原点镜像
    canvas.drawPath(_gridPath, _gridPaint);
    canvas.restore();
  }

  void _drawAxis(Canvas canvas, Size size) {
    canvas.drawPoints(
        PointMode.lines,
        [
          Offset(-size.width / 2, 0),
          Offset(size.width / 2, 0),
          Offset(0, -size.height / 2),
          Offset(0, size.height / 2),
          Offset(0, size.height / 2),
          Offset(0 - 7.0, size.height / 2 - 10),
          Offset(0, size.height / 2),
          Offset(0 + 7.0, size.height / 2 - 10),
          Offset(size.width / 2, 0),
          Offset(size.width / 2 - 10, 7),
          Offset(size.width / 2, 0),
          Offset(size.width / 2 - 10, -7),
        ],
        _gridPaint
          ..color = Colors.blue
          ..strokeWidth = 1.5);
  }

  Path _buildGridPath(Path path, Size size, {step = 20.0}) {
    for (int i = 0; i < size.height / 2 / step; i++) {
      path.moveTo(0, step * i);
      path.relativeLineTo(size.width / 2, 0);
    }
    for (int i = 0; i < size.width / 2 / step; i++) {
      path.moveTo(step * i, 0);
      path.relativeLineTo(
        0,
        size.height / 2,
      );
    }
    return path;
  }

  void _drawHelp(Canvas canvas) {
    canvas.drawPoints(PointMode.lines, pos, _helpPaint..strokeWidth = 1);
    canvas.drawPoints(PointMode.points, pos, _helpPaint..strokeWidth = 8);
  }

  void _drawSelectPos(Canvas canvas) {
    if (selectPos == null) return;
    canvas.drawCircle(
        pos[selectPos],
        10,
        _helpPaint
          ..color = Colors.green
          ..strokeWidth = 2);
  }
}