View the directory –>

The Flutter custom controls fall into three categories:

  • Combine controls to make a new widget by combining other widgets.
  • Paint control, fully drawn by using Canvas and Paint.
  • Inherit widgets, draw with RenderObject, but eventually draw with canvas.

This paper mainly

This article focuses on self-painting controls, because all widgets are drawn using Canvas and paint. Understanding both is helpful for tracing the principles of other widgets.

  • Canvas, Canvas
  • Brush Paint:

How to do?

  • Inheritance CustomPainter
  • Override paint method and shouldRepaint method
  • Paint provides canvas for drawing and size for sizing
  • ShouldRepaint is used to make sure that it is redrawn every time

Paint?

Canvas. DrawRect () draws a rectangle;
void drawRect(Rect rect, Paint paint)
Copy the code

Rect description of the rectangle

  • Rect.fromCenter({Offset center, double width, double height}) defines a rectangle according to the center point and the width and height.
  • Rect.fromcircle ({Offset center, double radius}), defines a rectangle based on the center point and radius
  • Rect.fromLTRB(this.left, this.top, this.right, this.bottom), rect. fromLTRB(this.left, this.top, this.right, this.bottom), left Bottom The distance between the bottom border and the top. Define a rectangle based on these four values.
  • Rect.fromLTWH(double left, double top, double width, double height), defines a rectangle based on the top left vertex and width.
  • Rect.fromPoints(Offset a, Offset B), defines a rectangle based on the top left and bottom right vertices.

Example:

class _MyHomePageState extends State<MyHomePage> { bool flag = true; void change(bool value) { setState(() { flag = value; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child: CustomPaint( size: Size(380, 560), painter: MyPainter(), ), ), ); } } class MyPainter extends CustomPainter{ @override void paint(Canvas canvas, Size size) { test01(canvas, size); } void test00(Canvas canvas, Size size) { var paint = new Paint() .. color = Colors.orange[200] .. style = PaintingStyle.fill .. isAntiAlias = true; DrawRect (Offset. Zero & size, paint); paint .. color = Colors.green; canvas.drawRect(Rect.fromCenter(width: 200,height: 200,center: Offset(150, 150)), paint); paint .. color = Colors.blue; canvas.drawRect(Rect.fromCircle(radius: 50,center: Offset(150, 150)), paint); paint .. color = Colors.redAccent; Canvas. DrawRect (the Rect. FromLTRB,10,200,100 (10), paint); paint .. color = Colors.pink; Canvas. DrawRect (the Rect. FromLTWH (10250100100), paint); paint .. color = Colors.grey; canvas.drawRect(Rect.fromPoints(Offset(250, 250),Offset(350, 300)), paint); } @override bool shouldRepaint(CustomPainter oldDelegate) { // TODO: implement shouldRepaint return true; }}Copy the code
Canvas. DrawRRect () draws rounded rectangles

void test01(Canvas canvas, Size size) { var paint = new Paint() .. color = Colors.orange[200] .. style = PaintingStyle.fill .. isAntiAlias = true; Rect = rect. fromCircle(center: Offset(200, 200), radius: 150); RRect rRect = RRect.fromRectAndRadius(rect, Radius.circular(30)); canvas.drawRRect(rRect, paint); }Copy the code
Canvas. DrawDRRect () draws a rounded rectangle

void test011(Canvas canvas, Size size) { var paint = new Paint() .. color = Colors.orange[200] .. style = PaintingStyle.fill .. isAntiAlias = true; Rect rect1 = rect. fromCircle(center: Offset(200, 200), radius: 140); Rect rect2 = Rect.fromCircle( center: Offset(200, 200), radius: 160); RRect rRect1 = RRect.fromRectAndRadius(rect1, Radius.circular(20)); RRect rRect2 = RRect.fromRectAndRadius(rect2, Radius.circular(20)); canvas.drawDRRect(rRect2, rRect1, paint); }Copy the code
The circle is drawn on the canvas. Methods like drawCircle ()
drawCircle(Offset c, double radius, Paint paint)
Copy the code
  • C center
  • The radius circle radius
  • Paint brush,

Example:

void test02(Canvas canvas, Size size) { var paint = new Paint() .. color = Colors.orange[200] .. style = PaintingStyle.fill .. isAntiAlias = true; DrawRect (Offset. Zero & size, paint); paint .. color = Colors.blue[200]; canvas.drawCircle(Offset(200, 200), 100, paint); }Copy the code
Canvas. DrawOval () draw the ellipse

void test022(Canvas canvas, Size size) { var paint = new Paint() .. color = Colors.orange[200] .. style = PaintingStyle.fill .. isAntiAlias = true; DrawRect (Offset. Zero & size, paint); paint.. color = Colors.blue[200]; canvas.drawOval( Rect.fromCenter(width: 200, height: 300, center: Offset(150, 250)), paint); }Copy the code
Canvas. DrawArc () arc
void drawArc(Rect rect, double startAngle, double sweepAngle, bool useCenter, Paint paint)
Copy the code
  • The enclosing rectangle of the ellipse to which the rect arc belongs, used to locate the arc
  • StartAngle, starting Angle, in radians, clockwise
  • SweepAngle, how many radians do I draw? The radian of a circle is 2PI, which is 2 times 3.14
  • UseCenter, whether to connect the arc to the center, forming a fan

void test08(Canvas canvas, Size size) { var paint = new Paint() .. color = Colors.orange[200] // .. style = PaintingStyle.fill .. style = PaintingStyle.stroke .. strokeWidth = 5 .. isAntiAlias = true; Rect rect = Rect.fromCircle( center: Offset(200, 200), radius: 100); Canvas. DrawArc (rect, 0, 3.14, false, paint); }Copy the code
Canvas. DrawPoints ()

Parameter PointModel:

  • Ui.pointmode. points,// separate points
  • Ui.pointmode. polygon,// All points are lined in the given order
  • Ui.pointmode. lines,// Draw a line starting at the first two points of a given array
void test06(Canvas canvas, Size size) { var paint = new Paint() .. color = Colors.orange[200] .. style = PaintingStyle.fill .. isAntiAlias = true; DrawRect (Offset. Zero & size, paint); paint .. style = PaintingStyle.stroke .. strokeWidth = 15 .. strokeCap = StrokeCap.round .. color = Colors.black; Canvas. drawPoints(// ui.pointmode. points,// single points // ui.pointmode. polygon,// all points are drawn in the given order ui.pointmode. lines,// draw a line, Start with the first two points of the given array [Offset(100, 100), Offset(300, 100), Offset(200, 300)], paint); }Copy the code
Canvas. DrawRawPoints ()

The difference with drawPoints is that points are represented in a different way. Parameter PointModel:

  • Ui.pointmode. points,// separate points
  • Ui.pointmode. polygon,// All points are lined in the given order
  • Ui.pointmode. lines,// Draw a line starting at the first two points of a given array

PointMode.points

PointMode.polygon

PointMode.lines

void _drawRawPoints(Canvas canvas, Size size) { Float32List points = Float32List.fromList([ -120, -20,-80, -80,-40, -40,0, -100,40, -140, 80, -160,120, -100]); Paint paint = new Paint(); paint .. color = Colors.redAccent[200] .. style = PaintingStyle.stroke .. strokeCap = StrokeCap.round .. strokeWidth = 20; canvas.drawRawPoints(PointMode.points, points, paint); // canvas.drawRawPoints(PointMode.lines, points, paint); // canvas.drawRawPoints(PointMode.polygon, points, paint); }Copy the code
Canvas.drawvertices () draws points with attributes

void _drawVertices(Canvas canvas, Size size) { Paint paint = new Paint(); paint .. color = Colors.blue[200] .. style = PaintingStyle.stroke .. strokeCap = StrokeCap.round .. strokeWidth = 20; canvas.drawVertices( Vertices(VertexMode.triangleFan, [Offset(50, 50), Offset(50, -50), Offset(-50, -50)], colors: RedAccent, Colors. BlueAccent, Colors. Green], // indices: [0,0,0], // [Offset(-50, -50), Offset(50, 50), Offset(-50, 50)] ), BlendMode.srcIn, paint); }Copy the code
Canvas. DrawLine draw a line ()
void drawLine(Offset p1, Offset p2, Paint paint)
Copy the code
  • The starting point of line P1
  • The end of line P2

Note:

  • Change the style of the brush to paintingstyle.stroke
  • You can adjust the line thickness strokeWidth = 3

Example:

void test03(Canvas canvas, Size size) { var paint = new Paint() .. color = Colors.orange[200] .. style = PaintingStyle.fill .. isAntiAlias = true; DrawRect (Offset. Zero & size, paint); paint .. color = Colors.black .. strokeWidth = 3 .. style = PaintingStyle.stroke; canvas.drawLine(Offset(100, 150), Offset(250, 150), paint); }Copy the code
Canvas. DrawPath () path

paint .. color = PaintingStyle.fill;

void test07(Canvas canvas, Size size) { var paint = new Paint() .. color = Colors.orange[200] .. style = PaintingStyle.fill .. isAntiAlias = true; Path = Path(); path.moveTo(100, 100); path.lineTo(100, 200); path.lineTo(200, 100); path.lineTo(200, 200); canvas.drawPath(path, paint); }Copy the code

paint .. color = PaintingStyle.stroke;

void test07(Canvas canvas, Size size) { var paint = new Paint() .. color = Colors.orange[200] // .. style = PaintingStyle.fill .. style = PaintingStyle.stroke .. strokeWidth = 5 .. isAntiAlias = true; Path = Path(); path.moveTo(100, 100); path.lineTo(100, 200); path.lineTo(200, 100); path.lineTo(200, 200); canvas.drawPath(path, paint); }Copy the code

path.close()

void test07(Canvas canvas, Size size) { var paint = new Paint() .. color = Colors.orange[200] // .. style = PaintingStyle.fill .. style = PaintingStyle.stroke .. strokeWidth = 5 .. isAntiAlias = true; Path = Path(); path.moveTo(100, 100); path.lineTo(100, 200); path.lineTo(200, 100); path.lineTo(200, 200); path.close(); canvas.drawPath(path, paint); }Copy the code
Canvas. DrawColor () color
void drawColor(Color color, BlendMode blendMode)
Copy the code
  • BlendMode is the blending mode of colors

void test04(Canvas canvas, Size size) { var paint = new Paint() .. color = Colors.orange[200] .. style = PaintingStyle.fill .. isAntiAlias = true; DrawColor (color.blue [200], blendmode.srcin); canvas.drawRect(Offset.zero & size/2, paint); }Copy the code
Canvas. DrawPaint () Uses paint to draw the canvas

void _drawPaint1(Canvas canvas, Size size) { Path path = Path(); path.lineTo(80, 80); path.lineTo(-80, 80); path.close(); canvas.clipPath(path); canvas.drawPaint(new Paint().. color = Colors.blueAccent); }Copy the code
Canvas. DrawShadow () draw a shadow
void drawShadow(Path path, Color color, double elevation, bool transparentOccluder)
Copy the code
  • Path shaded area
  • Color shadow color
  • Elevation Shadow height, usually lower to the right
  • TransparentOccluder If the blocking object is not opaque, the blocking “transparentOccluder” parameter should be true. Baidu translation, did not understand what meaning

void test09(Canvas canvas, Size size) { var paint = new Paint() .. color = Colors.orange[200] .. style = PaintingStyle.fill .. strokeWidth = 5 .. isAntiAlias = true; Path path = Path(); path.moveTo(100, 100); path.lineTo(100, 200); path.lineTo(200, 100); path.lineTo(200, 200); canvas.drawPath(path, paint); canvas.drawShadow(path, Colors.orange, 10, true); }Copy the code

Draw the text

textpainter.paint()
void _drawTextPaint(Canvas canvas) {
    var textpainter = TextPainter(
        text: TextSpan(
            text: '123456', style: TextStyle(fontSize: 40, color: Colors.red)),
        textAlign: TextAlign.center,
        textDirection: TextDirection.ltr);
    textpainter.layout();
    Size size = textpainter.size;
    textpainter.paint(canvas, Offset(-size.width / 2, -size.height / 2));
  }
Copy the code
canvas.drawParagraph()

void _drawTextWithParagraph(Canvas canvas) { TextAlign textAlign = TextAlign.center; var builder = ui.ParagraphBuilder(ui.ParagraphStyle( textAlign: textAlign, fontSize: 40, textDirection: TextDirection.ltr, maxLines: 1, )); var builder1 = ui.ParagraphBuilder(ui.ParagraphStyle( textAlign: TextAlign.right, fontSize: 40, textDirection: TextDirection.ltr, maxLines: 1)); builder.pushStyle( ui.TextStyle( color: Colors.black87, textBaseline: ui.TextBaseline.alphabetic), ); builder1.pushStyle(ui.TextStyle( color: Colors.red, textBaseline: ui.TextBaseline.alphabetic)); builder.addText("Flutter Can"); builder1.addText("ssssssss"); ui.Paragraph paragraph = builder.build(); ui.Paragraph paragraph1 = builder1.build(); paragraph.layout(ui.ParagraphConstraints(width: 300)); paragraph1.layout(ui.ParagraphConstraints(width: 400)); canvas.drawParagraph(paragraph, Offset(-100, 0)); canvas.drawParagraph(paragraph1, Offset(-200, -110)); canvas.drawRect(Rect.fromLTRB(0, 0, 300, 40), _paint.. color = Colors.blue.withAlpha(33)); }Copy the code

drawing

Create images in the root directory to store the image resources and declare assets in pubspec.yamlNote the indentation. Assets are preceded by 2 Spaces, – by 2 Spaces, and – by 1 spaceImage to load

Future<ui.Image> loadImageFromAssets(String path) async {ByteData data = await rootBundle.load(path); List<int> bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); return decodeImageFromList(bytes); } @override void initState() {super.initstate (); _loadImage(); } void _loadImage() async { _image = await loadImageFromAssets('images/a2.png'); setState(() {}); }Copy the code
Drawing canvas. DrawImage ()

void _drawImage(Canvas canvas) {
    if (image != null) {
      canvas.drawImage(
          image, Offset(-image.width / 2, -image.height / 2), _paint);
    }
  }
Copy the code
Canvas.drawImageRect () draws part of the image to the specified area

void drawImageRect(Image image, Rect src, Rect dst, Paint paint)

  • Image, the source of the image
  • SRC, which region to select from the image
  • DST, what region do I draw

canvas.drawImageRect(
          image,
          Rect.fromCenter(
              center: Offset(image.width / 2, image.height / 2),
              width: 180,
              height: 180),
          Rect.fromLTRB(0, 0, 200, 200),
          _paint);
Copy the code
Canvas. DrawImageNine (). 9

void drawImageNine(Image image, Rect center, Rect dst, Paint paint)

  • Image, the source image
  • Cennter, set the stretch zone
  • DST, where do I draw it

When filling the DST, if the width or height is insufficient, the DST is divided into three rows and three columns centered around the center, with the middle row and the middle column being the stretch area

Canvas. DrawImageNine (image, rect. fromCenter(center: Offset(image.width / 2, image.height-150.0), width: 1, height: Offset(center: 0, 0,), width: 430, height: 430), _paint);Copy the code
canvas.drawAtlas()

From a diagram, capture the recTS image of the specified area and place it in the trasForms area

void drawAtlas(Image atlas, List transforms, List rects, List? colors, BlendMode? blendMode, Rect? cullRect, Paint paint)

void _drawAtlas(Canvas canvas) { Paint paint = Paint() .. style = PaintingStyle.stroke .. strokeWidth = 2; Rect cullRect = Rect.fromLTRB(50, 50, 50, 50); Canvas. DrawAtlas (image, [RSTransform fromComponents (rotation: - 10, scale: 1.0, anchorX: 0, anchorY: 0, translateX: 10, translateY: 10), RSTransform fromComponents (rotation: 40, scale: 1.0, anchorX: 0, anchorY: 0, translateX: 90, translateY: 90) ], [Rect.fromLTWH(50, 50, 100, 100), Rect.fromLTWH(150, 150, 100, 100)], [Colors.redAccent, Colors.blueAccent], BlendMode.srcIn, cullRect, paint); }Copy the code
Canvas. DrawRawAtlas ()??? To be processed

The canvas cutting

After clipping, all operations of canvas are limited to the clipping area

Canvas.clippath () clipped to the specified path

void _clipPath(Canvas canvas, Size size) { Path path = Path(); path.lineTo(80, 80); path.lineTo(-80, 80); path.close(); canvas.clipPath(path); canvas.drawPaint(new Paint().. color=Colors.redAccent); }Copy the code
Canvas. ClipRect () clipped to a rectangle

Note the clipOp parameter:

  • Clipop. intersect clips inside the canvas

  • Clipop. difference clips out of the canvas

void _clipRect(Canvas canvas, Size size) { var rect = Rect.fromCenter(center: Offset.zero,width: 180,height: 120); canvas.clipRect(rect,doAntiAlias: true,clipOp: ui.ClipOp.intersect); // Canvas. ClipRect (rect,doAntiAlias: true,clipOp: uI.clipop.difference); DrawPaint (new Paint().. color=Colors.redAccent); }Copy the code
Canvas.clipdrect () clipped to a rounded rectangle

void _clipDRect(Canvas canvas, Size size) { var rect = Rect.fromCenter(center: Offset.zero,width: 180,height: 120); canvas.clipRRect(RRect.fromRectAndRadius(rect, Radius.circular(30)),doAntiAlias: true); // canvas.drawPaint(new Paint().. color=Colors.redAccent); }Copy the code

The canvas of the layer

save(),saveLayer(),restore()
  • Canvas.save (), which saves the previous drawing and the state of the canvas
  • Canvas. SaveLayer (rect, paint), save the previous drawing and create a new layer on which to draw later. The rect parameter determines the scope of the layer and can only be drawn within this scope. Drawings outside this scope are not shown. The paint parameter indicates the blending mode of its blendMode with the previous layer.
  • Canvas.restore (), which merges this method with the previous operation between save() or saveLayer(), save and restore, saveLayer and restore appear in pairs, save without restore, and an error will be reported.

void test10(Canvas canvas, Size size) { var paint = new Paint() .. color = Colors.orange[200] .. style = PaintingStyle.fill .. isAntiAlias = true; canvas.drawPaint(paint); // Paint.. color = Colors.blue[200]; canvas.drawRect(Offset.zero&size, paint); // Draw the area paint.. color = Colors.green[200]; Canvas. DrawRect (the Rect. FromCircle (center: Offset (200200), the radius: 100), paint); // Save the previously drawn canvas. Save (); Color = color.grey; // Draw a rectangle on top of the previous layer. Canvas. DrawRect (the Rect. FromCircle (center: Offset (250300), the radius: 100), paint); / / save canvas. Restore (); Paint. BlendMode = blendmode. SRC; // create a new layer and specify the new layer area and blend mode canvas.saveLayer(rect. fromCircle(center:Offset(270,350),radius:100), paint); paint .. color = Colors.orange; DrawRect (rect. fromCircle(center:Offset(270,350),radius:150), paint); / / save canvas. Restore (); }Copy the code

As you can see, draw a rectangle on the new layer that is out of the layer’s range. The contents that are out of the range are not displayed, only those within the layer’s range are displayed

Canvas. GetSaveCount () gets the number of layers in the save stack

GetSaveCount () = +1 for each save() or saveLayer();

Each time restore() is restored, getSaveCount() takes the value -1.

Such as:

print("canvas1="+canvas.getSaveCount().toString()); // canvas1=1 canvas.save(); canvas.save(); print("canvas2="+canvas.getSaveCount().toString()); // canvas2= 3 canvas.restore(); print("canvas3="+canvas.getSaveCount().toString()); // canvas3= 2 canvas.restore(); print("canvas4="+canvas.getSaveCount().toString()); // canvas4= 1Copy the code

The canvas transform

Canvas. Translate () translation

Canvas. Translate (dx,dy) refers to the distance by which the entire canvas is shifted dx dy towards xy

Void test11(Canvas Canvas, Size Size) {var paint = new paint ().. color = Colors.orange[200] .. style = PaintingStyle.fill .. isAntiAlias = true; // Canvas. DrawRect (Offset. Zero&size /2, paint); // Draw the area paint.. color = Colors.green[200]; Canvas. DrawRect (the Rect. FromCircle (center: Offset (100100), the radius: 50), paint); // Draw a rectangle // Save the previous drawing content canvas.save(); canvas.translate(200, 200); // Shift paint. Color = color.red [200]; canvas.drawRect(Offset.zero&size/2, paint); // Draw the Canvas area paint. Color = color.green [200]; Canvas. DrawRect (the Rect. FromCircle (center: Offset (100100), the radius: 50), paint); // Draw a rectangle // Save canvas.restore(); }Copy the code

After the translation, the content drawn by the canvas is displayed at the position after the translation.

Canvas. Scale (sx, sy) scale

Canvas. scale(0.5, 2): Halve the X-axis and double the Y-axis to see the effect:

Void test12(Canvas Canvas, Size Size) {var paint = new paint ().. color = Colors.orange[200] .. style = PaintingStyle.fill .. isAntiAlias = true; // Canvas. DrawRect (Offset. Zero&size /2, paint); // Draw the area paint.. color = Colors.green[200]; Canvas. DrawRect (the Rect. FromCircle (center: Offset (100100), the radius: 50), paint); // Draw a rectangle // Save the previous drawing content canvas.save(); Canvas. Scale (0.5, 0.5); Color = color.red [200]; canvas.drawRect(Offset.zero&size/2, paint); // Draw the Canvas area paint. Color = color.green [200]; Canvas. DrawRect (the Rect. FromCircle (center: Offset (100100), the radius: 50), paint); // Draw a rectangle // Save canvas.restore(); }Copy the code
Canvas. Rotate (value)
  • Value: PI =3.14 (180 clockwise) Read: PI *2 (360 degrees clockwise)
  • The center of the rotation is the left vertex of the canvas, which is 0,0.

To see the effect: first translation, then rotation:

Void test13(Canvas Canvas, Size Size) {var paint = new paint ().. color = Colors.orange[200] .. style = PaintingStyle.fill .. isAntiAlias = true; // Canvas. DrawRect (Offset. Zero&size /2, paint); // Draw the area paint.. color = Colors.green[200]; Canvas. DrawRect (the Rect. FromCircle (center: Offset (100100), the radius: 50), paint); // Draw a rectangle // Save the previous drawing content canvas.save(); canvas.translate(200, 200); Rotate (1); rotate(2); Color = color.red [200]; canvas.drawRect(Offset.zero&size/2, paint); // Draw the Canvas area paint. Color = color.green [200]; Canvas. DrawRect (the Rect. FromCircle (center: Offset (100100), the radius: 50), paint); // Draw a rectangle // Save canvas.restore(); }Copy the code
Canvas. Skew (sx, sy) diagonally

It’s tangent of sx, tangent of sy.

Canvas Canvas, Size Size) {var paint = new paint ().. color = Colors.orange[200] .. style = PaintingStyle.fill .. isAntiAlias = true; // Canvas. DrawRect (Offset. Zero&size /2, paint); // Draw the area paint.. color = Colors.green[200]; Canvas. DrawRect (the Rect. FromCircle (center: Offset (100100), the radius: 50), paint); // Draw a rectangle // Save the previous drawing content canvas.save(); canvas.skew(pi/4, 0); // // canvas.skew(tan(45), 0); // paint.color = Colors.red[200]; canvas.drawRect(Offset.zero&size/2, paint); // Draw the Canvas area paint. Color = color.green [200]; Canvas. DrawRect (the Rect. FromCircle (center: Offset (100100), the radius: 50), paint); // Draw a rectangle // Save canvas.restore(); }Copy the code
Canvas.transform () matrix transformation

The four canvas transformations above are ultimately transform-based.

void _transform(Canvas canvas, Size size) { Path path = Path(); path.lineTo(60, 60); path.lineTo(-60, 60); path.lineTo(60, -60); path.lineTo(-60, -160); path.close(); canvas.drawPath(path, _paint); // canvas.translate(140, 0); canvas.transform(Float64List.fromList([ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 120, 0, 0, 1])); canvas.drawPath( path, _paint .. style = PaintingStyle.stroke .. strokeWidth = 2); }Copy the code