The Flutter Canvas will learn as a series of original portals:

  • Learn the basics of Flutter Canvas
  • Drawing of Flutter Canvas learning
  • Flutter Canvas learning words and pictures

preface

In general UI framework, text and picture drawing are more complicated, so I will explain it in a separate article.

Paragraph

void drawParagraph(Paragraph paragraph, Offset offset)

Paragraph is a class that is used to draw text in a Flutter. All the text in a Flutter is drawn through it, including the input box. This shows its power.

A Paragraph is a class with no constructor; it simply provides a host for the final rendering. What we really need to deal with is a class that ParagraphBuilder.

The ParagraphBuilder class takes one parameter, which is a ParagraphStyle class that sets basic font styles, such as font direction, alignment, font thickness, and so on. Next, we draw text in a few steps

First, generate ParagraphStyle class

import 'dart:ui' as ui;
final paragraphStyle = ui.ParagraphStyle(
  // Font orientation. Some languages are formatted from right to left
  textDirection: TextDirection.ltr,
  // Font alignment
  textAlign: TextAlign.justify,
  fontSize: 14,
  maxLines: 2.// Display the prompt when the font size exceeds
  ellipsis: '... ',
  fontWeight: FontWeight.bold,
  fontStyle: FontStyle.italic,
  height: 5.// Whether the height applies to the top and bottom of the font when we set [textstyle.height]
  textHeightBehavior:
      TextHeightBehavior(applyHeightToFirstAscent: true,applyHeightToLastDescent: true));
Copy the code

Second, you generate ParagraphBuilder based on ParagraphStyle

final paragraphBuilder = ui.ParagraphBuilder(paragraphStyle);
Copy the code

Third, add text. The ParagraphBuilder class has an addText method specifically for receiving text

paragraphBuilder.addText('JSShou learning Canvas');
Copy the code

Step 4, retrieve the Paragraph class through build

var paragraph = paragraphBuilder.build();
Copy the code

Step 5: Layout according to the width and height

paragraph.layout(ui.ParagraphConstraints(width: 300));
Copy the code

Step 6: Paint

canvas.drawParagraph(paragraph, Offset(50.50));
Copy the code

The whole code is as follows

/ / the first step
final paragraphStyle = ui.ParagraphStyle(
    // Font orientation. Some languages are formatted from right to left
    textDirection: TextDirection.ltr,
    // Font alignment
    textAlign: TextAlign.justify,
    fontSize: 14,
    maxLines: 2.// Display the prompt when the font size exceeds
    ellipsis: '... ',
    fontWeight: FontWeight.bold,
    fontStyle: FontStyle.italic,
    height: 5.// Whether the height applies to the top and bottom of the font when we set [textstyle.height]
    textHeightBehavior:
        TextHeightBehavior(applyHeightToFirstAscent: true,applyHeightToLastDescent: true));
// Step 2 and step 3
finalparagraphBuilder = ui.ParagraphBuilder(paragraphStyle) .. addText('ParagraphBuilder class takes one parameter, which is a ParagraphStyle class that sets basic font styles, such as font direction, alignment, font thickness, and so on. Let's draw the text in a few steps. ');
/ / step 4
var paragraph = paragraphBuilder.build();
/ / step 5
paragraph.layout(ui.ParagraphConstraints(width: 300));
// Draw an auxiliary rectangle (you can use paragraph. Width and paragraph. Height to get the width and height of the drawn text)
canvas.drawRect(
    Rect.fromLTRB(50.50.50 + paragraph.width, 50 + paragraph.height),
    paint);
/ / step 6
canvas.drawParagraph(paragraph, Offset(50.50));
Copy the code

The effect is as follows:

TextPainter

Paragraph, I have to say TextPainter. TextPainter is encapsulated by Paragraph, which provides more power than Paragraph

  • By passing inTextSpanTo achieve a variety of different effects of fonts to support rich text
  • Don’t likeParagraphYou must set a width, which can be used without initializing the widthTextPainter.widthTo get the actual render width (actuallyParagraphIt can be done, but it encapsulates a layer of development thinking that is closer to us.)

It is also simple to use

var textPainter = TextPainter(
  text: TextSpan(
      text:
          "Rich text can be supported by a variety of fonts with different effects.",
      style: TextStyle(color: Colors.white,fontSize: 20)),
  textDirection: TextDirection.rtl,
  textWidthBasis: TextWidthBasis.longestLine,
  maxLines: 2.)// You can pass minWidth, maxWidth to limit its width. If not, the text will be drawn on a single line
..layout();
var startOffset = 50.0;
TextPainter. Width and textPainter. Height can be used to get the width of the text drawn before the text is drawn
canvas.drawRect(
    Rect.fromLTRB(startOffset, startOffset, startOffset + textPainter.width,
        startOffset + textPainter.height),
    paint);
textPainter.paint(canvas, Offset(startOffset, startOffset));
Copy the code

drawImage

void drawImage(Image image, Offset offset, Paint paint)

We can draw an Image by drawImage. The Image here is not our usual Image, it is the Image in the Dart: UI library, which holds some basic information about the Image and interacts directly with the engine.

To use this method, you need to load an image. There are many ways to load an image, but I’ll show you one way

load('assets/test.png');
// ...
Future<ui.Image> load(String asset) async {
  ByteData data = await rootBundle.load(asset);
  ui.Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List(),targetWidth: 300,targetHeight: 300);
  ui.FrameInfo fi = await codec.getNextFrame();
  return fi.image;
}
Copy the code

Since image loading is an asynchronous process, you can’t load them in the paint method of CustomPaint (because canvas is disposed after paint, and Object has been disposed if you use canvas after asynchronous method), You need to use a StatefulWidget externally that passes the Image you get into the CustomPaint after loading. At this point, use Canvas. drawImage to load.

drawImageRect

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

  • SRC: Capture an image area with the starting point relative to the upper left corner of the image
  • DST: Draw an area on the canvas to draw the captured image. The image may be pulled up
// Draw the original image
canvas.drawImage(image, Offset(50.50), paint);
// Image area
Rect rect = Rect.fromCenter(
        center: Offset(image.width / 2, image.height / 2),
        width: image.width / 2,
        height: image.height / 2);
// Draw the auxiliary line, move it down and right 50, because the original is relative to Offset(50,50).
canvas.drawRect(rect.shift(Offset(50.50)), paint);
// go
canvas.drawImageRect(
    image,
    rect,
    Rect.fromLTWH(50.500.100.100),
    paint);
Copy the code

drawImageNine

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

This method is similar to the.9 image in Android, where certain areas are not distorted when the image is stretched, and real areas can be drawn by ourselves.

canvas.drawImage(image, Offset(50.50), paint);
Rect rect = Rect.fromCenter(
        center: Offset(image.width / 2, image.height / 2),
        width: image.width / 2,
        height: image.height / 2);
canvas.drawRect(rect.shift(Offset(50.50)), paint);
Rect dst = Rect.fromLTWH(50.500.200.200);
canvas.drawImageNine(image, rect, dst, paint);
Copy the code

If we change the DST, which is the image drawing area

Rect dst = Rect.fromLTWH(50.500.400.200);
Copy the code

You are smart enough to have found that the image is stretched and compressed in a certain way. The blue area of the original image above is the position that can be compressed and stretched, and the upper, lower, left and right parts of the blue area also exist the position that can be stretched, and the four corners will remain unchanged without distortion.

drawPicture

This method is drawn by passing in an instance of a Picture, which is constructed by PictureRecorder.

// Start recording Picture
ui.PictureRecorder recorder = ui.PictureRecorder();
Canvas canvas = new Canvas(recorder);

// Call the Canvas drawing interface to draw a circle
canvas.drawCircle(
    Offset(200.200), 100, Paint().. color = Colors.yellow);// 绘制结束,生成Picture
Picture picture = recorder.endRecording();
Copy the code

“Picture” does not refer to our traditional Picture, Picture correlation is expressed by Image, Picture is drawn by any graph (drawLine, drawRect, etc.). The above is just the simplest example of creating a Picture. We create a Canvas object to record the drawing data, and call Recorder. EndRecording to end the recording and return a Picture object. As a special note, the Canvas provides the drawPicture method and does not expect us to use PictureRecorder to record and draw the UI(in fact, if we did this in paint, we would get an error. CustomPaint provides a Canvas object, which does not require new), and we can use this method when we get a Picture object somewhere else. So we need to execute the above code in the StatefulWidget and pass the resulting Picture object into CustomPaint for drawing.

class MyCanvas extends CustomPainter {
  final ui.Picture picture;

  MyCanvas(this.picture);
  @override
  void paint(Canvas canvas, Size size) async {
    if(picture ! =null) canvas.drawPicture(picture);
  }
  / /...
}
Copy the code

The next chapter will introduce Canvas graph transformation, such as rotation, translation, zooming, etc. If you are interested, please pay attention

  • zhihu
  • Personal blog
  • GitHub