Custom UI series columns





Drawing capability of Canvas

  • The Canvas provided by the Flutter system can draw regular geometric shapes and transform shapes

Regular geometry

Point drawPoints

void drawPoints(PointMode pointMode, List<Offset> points, Paint paint)
Copy the code
  • pointMode: Drawing mode
    • PointMode.pointsPoint, draw each point separately
    • PointMode.linesA line segment, every two points in a line segment
    • PointMode.polygonA line. All the points make a line
  • points: a collection of points
  • paintBrush:


_paint.strokeCap=StrokeCap.round; 
Copy the code

When you set the strokeCap property of the brush to Round, you draw dots


void drawRawPoints(PointMode pointMode, Float32List points, Paint paint)
Copy the code

The drawRawPoints() method is basically similar to drawPoints() in that the set of points passed in is Float32List


  • The sample code
void paint(Canvas canvas, Size size) { canvas.drawPoints(PointMode.points, [Offset(0, 0), Offset(30, 0), Offset(60, 0), Offset(90, 0)], _paint); _paint.strokeCap=StrokeCap.round; Canvas. Points(pointmode. points, [Offset(0, 50),Offset(30, 50),Offset(60, 50),Offset(90,50)], _paint); canvas.drawPoints(PointMode.lines, [Offset(0, 100), Offset(30, 100), Offset(60, 100),Offset(90, 100)], _paint); canvas.drawPoints(PointMode.polygon, [Offset(0, 150), Offset(30, 150), Offset(60, 150),Offset(90, 150)], _paint); Float32List points = Float32List. FromList ([0, 200, 30,200,60,200,90,200]); canvas.drawRawPoints(PointMode.points, points, _paint); }Copy the code
  • Effect of the sample


Line drawLine

void drawLine(Offset p1, Offset p2, Paint paint)
Copy the code
  • P1: the starting point
  • P2: the end point

Set strokeWidth for paint to change the line width


  • The sample code
Void paint(Canvas Canvas, Size Size) {Canvas. DrawLine (Offset. Zero, 100,0), _paint); _paint.strokeWidth=2; Canvas. DrawLine (Offset (0; seven), Offset (100), _paint); }Copy the code


  • Effect of the sample


rectangular

/// rectangular
void drawRect(Rect rect, Paint paint) 
/// The rounded rectangle
void drawRRect(RRect rrect, Paint paint) 
/// Two rectangular clipped rings
void drawDRRect(RRect outer, RRect inner, Paint paint)
Copy the code

Set the style property for paint, and draw the border or fill mode

Rect has multiple named constructors

  • The position of the rectangle is determined by the position of the four sides
Rect.fromLTRB(this.left, this.top, this.right, this.bottom) 
Copy the code
  • Construct a rectangle from its left and top edges, its width, and its height
Rect.fromLTWH(double left, double top, double width, double height) 
Copy the code
  • Construct a rectangle from its center point, width, and height
Rect.fromCenter({ required Offset center, required double width, required double height }) 
Copy the code
  • Construct a rectangle enclosing the given circle
Rect.fromCircle({ required Offset center, required double radius })  
Copy the code
  • Construct a rectangle with the upper left and lower right points
Rect.fromPoints(Offset a, Offset b)
Copy the code


  • The sample code
  @override
  void paint(Canvas canvas, Size size) {

    canvas.drawRect(Rect.fromCenter(center: Offset(30, 50), width: 60, height: 60), _paint);
    
    _paint.style = PaintingStyle.stroke;
    canvas.drawRect(Rect.fromCenter(center: Offset(100, 50), width: 60, height: 60), _paint);

    canvas.drawRRect(RRect.fromRectXY(Rect.fromCenter(center: Offset(170, 50), width: 60, height: 60), 8, 8), _paint);
    
    _paint.style = PaintingStyle.fill;
    var outer = RRect.fromRectXY(Rect.fromCenter(center: Offset(240, 50), width: 60, height: 60), 0, 0);
    var inner = RRect.fromRectXY(Rect.fromCenter(center: Offset(240, 50), width: 50, height: 50), 30, 30);
    canvas.drawDRRect(outer, inner, _paint);
  }
Copy the code


  • Effect of the sample


Circle and arc

void drawCircle(Offset centre, double radius, Paint paint)
Copy the code

Methods like drawCircle draw round

  • centreCenter:
  • radiusRadius of:


void drawOval(Rect rect, Paint paint)
Copy the code

Draw the ellipse

  • rectThe range of the ellipse, the ellipse is the tangent ellipse of this rectangle


void drawArc(Rect rect, double startAngle, double sweepAngle, bool useCenter, Paint paint)
Copy the code

Arcs and sectors

  • rect: The range of the ellipse in which the arc is located
  • startAngle: Starting Angle, in radian units, that is, 2*π=360°. 0 is the right-most horizontal direction clockwise.
  • sweepAngle: Arc The arc of a line
  • useCenter: sector when true, arc when false


  • The sample code
  @override
  void paint(Canvas canvas, Size size) {
    canvas.drawCircle(Offset(50, 50), 50, _paint);
    canvas.drawOval(Rect.fromLTRB(130, 0, 300, 100), _paint);
    canvas.drawArc(Rect.fromLTWH(0, 120, 150, 100), 0, pi/2, false, _paint);
    canvas.drawArc(Rect.fromLTWH(160, 120, 150, 150), 3*pi/2, pi/3, true, _paint);
  }
Copy the code


  • Effect of the sample


Image drawImage

void drawImage(Image image, Offset offset, Paint paint)
Copy the code
  • imageThis is not a Wdiget image, but a dart. UI image
  • offset: Position in the upper left corner of the picture

Method to get uI.image

Future<void> loadImage() async {
  image = await _loadImageByProvider(AssetImage("images/coupons.png"));
  setState(() {});
}

Future<ui.Image> _loadImageByProvider(ImageProvider provider, {ImageConfiguration config = ImageConfiguration.empty}) async {
  Completer<ui.Image> completer = Completer<ui.Image>();
  late ImageStreamListener listener;
  ImageStream stream = provider.resolve(config);
  listener = ImageStreamListener((ImageInfo frame, bool sync) {
    final ui.Image image = frame.image;
    completer.complete(image);
    stream.removeListener(listener);
  });
  stream.addListener(listener);
  return completer.future;
}
Copy the code


canvas.drawImageRect(image, src, dst, paint)
Copy the code

Select part of the image to the target region

  • The sample code
@override void paint(Canvas canvas, Size size) { if (image ! = null) {// Draw canvas. DrawImage (image! , Offset.zero, _paint); Var SRC = rect.fromltwh (0, 0, image?.width. ToDouble ()?? Sie.width, image?.height.toDouble() ?? size.height); var dst = Rect.fromLTWH(150, 0, 70, 70); canvas.drawImageRect(image! , src, dst, _paint); }}Copy the code
  • Example effects (Set the size of the target area to allow the image to be zoomed in and out)


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

Draw an image center: stretchable or compressed area DST: the area on the canvas

The image is segmented by drawing two horizontal and two vertical lines. The image is drawn into nine parts, where the center parameter describes the rectangle formed by the four points where these four lines intersect each other. When stretched or compressed, the four angles remain the same, and the horizontal and vertical directions corresponding to the center change

  • The sample code
@override
void paint(Canvas canvas, Size size) {
  if (image != null) {
    var center = Rect.fromLTWH(50, 50, 20, 20);
    var dst = Rect.fromLTWH(0, 0, 128*2, 128);
    canvas.drawImageNine(image!, center, dst, _paint);
    canvas.drawImage(image!, Offset(0,100), _paint);
  }
}
Copy the code
  • Effect of the sample


Colors and Shadows

void drawColor(Color color, BlendMode blendMode)
Copy the code

Color background, blendMode: color blending mode

void drawShadow(Path path, Color color, double elevation, bool transparentOccluder)
Copy the code
  • path: Shadow path
  • color: Shadow color
  • elevation: Height of the shadow
  • transparentOccluder“: Official document translation meansWhether the occluding object is transparent. Personally, because shadows are created when a light source is blocked above the canvas,transparentOccluderfalseWhen, the shield is opaque.

  • The sample code
@override void paint(Canvas canvas, Size size) { Path path = Path(); var rect = Rect.fromLTWH(10, 10, 150, 45); path.addRect(rect); canvas.drawShadow(path, Colors.red, 5, false); Paint paint = Paint().. color = Colors.red; canvas.drawRect(rect, paint); }Copy the code
  • Effect of the sample


Path

Path Path is an important and commonly used tool in Canvas drawing ability. It is composed of multiple complex one-dimensional paths, including points, lines, arcs and Bezier curves, which can be open or closed. The method of adding subpaths to Path is similar to the method of drawing on Canvas above, which mainly includes points, lines, rectangles, circles and arcs.


Some relatively simple apis

// Move the brush to the specified position
void moveTo(double x, double y)  
// Connect a line from the current position to the specified position
void lineTo(double x, double y)
// Add a rectangle to path
void addRect(Rect rect)
// Add rounded rectangles
void addRRect(RRect rrect)
// Add an ellipse to path, if Rect is a square, then add a circle
void addOval(Rect oval)
// Add an arc to path in radians
void addArc(Rect oval, double startAngle, double sweepAngle)
// Add some points to the path, which will be connected to a line segment. [close] closes or not. If true, the last point is connected to the first point
void addPolygon(List<Offset> points, bool close)
// Add another path to the current path
void addPath(Path path, Offset offset, {Float64List? matrix4})
// Reset Path to restore Path to its original state with the current point as the starting point
void reset()
// Close the image
void close()
Copy the code
  • The sample code
@override void paint(Canvas canvas, Size size) { var path = Path(); Path. MoveTo (10, 10); path.lineTo(100, 10); path.lineTo(100, 60); path.lineTo(10, 60); // Finally the line will be drawn from the current position to the starting point path.close(); canvas.drawPath(path, _paint); }Copy the code


arc

void arcTo(Rect rect, double startAngle, double sweepAngle, bool forceMoveTo)
Copy the code

So if you draw an arc, forceMoveTo means when you draw that arc, do you drag the pen to the starting point or do you lift the pen to the starting point

@override void paint(Canvas canvas, Size size) { var path = Path(); Path. The moveTo (0, 0); Path. The lineTo (100, 0); path.arcTo(Rect.fromLTWH(0, 0, 100, 100), 0, pi, true); canvas.drawPath(path, _paint); }Copy the code


Bessel curve

// Second order curve
void conicTo(double x1, double y1, double x2, double y2, double weight)
Copy the code

Plot bezier curves

  • x1andy1Control points:
  • x2andy2: Target point of the curve
  • weight: Weight, greater than 1, then the curve is hyperbola; If the weight is equal to 1, it is a parabola; If it’s less than 1, it’s an ellipse
void cubicTo(double x1, double y1, double x2, double y2, double x3, double y3)
Copy the code

Third order Bezier curve

  • x1andy1: Control point 1
  • x2andy2: Control point 2
  • x3andy3: Target point of the curve
  • The sample code
@override void paint(Canvas canvas, Size size) { var path = Path(); path.moveTo(80, 40); Path. ConicTo (160, 120, 240, 40, 1); canvas.drawPath(path, _paint); var path2 = Path(); path2.moveTo(80, 160); path2.cubicTo(120, 200, 160, 100, 240, 160); canvas.drawPath(path2, _paint); }Copy the code
  • Effect of the sample


Relative coordinates

void relativeMoveTo(double dx, double dy)
void relativeLineTo(double dx, double dy)
Copy the code

Relative *** relative to the current position.


The boundary of the path

Rect getBounds()
Copy the code

Gets the path boundary range and returns a Rect.

  • The sample code
@override void paint(Canvas canvas, Size size) { var path2 = Path(); path2.moveTo(80, 80); path2.lineTo(160, 80); path2.arcTo(Rect.fromLTWH(160, 80, 80, 80), 0, pi, false); path2.lineTo(0, 80); path2.close(); canvas.drawPath(path2, _paint); // Path bounds var bounds = path2.getbounds (); _paint.color=Colors.black; canvas.drawRect(bounds, _paint); }Copy the code
  • Effect of the sample


PathMetric

PathMetrics computeMetrics({bool forceClosed = false})
Copy the code

Returns a collection of PathMetric objects with changed paths

PathMetric is a tool for measuring and extracting paths, and you can get a lot of information about your path

  • PathMetric.length: Indicates the total length of the path
  • PathMetric.isClosed: Indicates whether the current path is a closed path
  • PathMetric.contourIndex: Indicates the index of the current path
  • Tangent? getTangentForOffset(double distance): Returns information about a point with a point length.

    For example, the getTangentForOffset (50) method of a path with length =100 computes the position and Angle of the point with length 50. The Angle is the Angle between the tangent of the point and the positive direction of the x axis.

  • The sample code
@override void paint(Canvas canvas, Size size) { var path = Path(); path.moveTo(0, 0); path.addOval(Rect.fromLTWH(0, 0, 160, 160)); canvas.drawPath(path, _paint); PathMetric pathMetric = path.computeMetrics().first; print(pathMetric.length); Print (pathmetry.isclosed); // true print(pathMetric.contourIndex); // 0 var tangentForOffset = pathMetric.getTangentForOffset(100); _paint.strokeWidth=10; canvas.drawPoints(ui.PointMode.points, [tangentForOffset!.position], _paint); print(tangentForOffset.angle); }Copy the code
  • Effect of the sample

Canvas out

void clipPath(Path path, {bool doAntiAlias = true})
void clipRect(Rect rect, { ClipOp clipOp = ClipOp.intersect, bool doAntiAlias = true })
void clipRRect(RRect rrect, {bool doAntiAlias = true})
Copy the code

Canvas clipping, clipping the specified part and drawing over the rest

  • ClipOp: clipop. difference, minus the target region; Clipop. intersect preserves the target area

  • DoAntiAlias: Whether anti-aliasing effect

  • The sample code

@override void paint(Canvas canvas, Size size) { canvas.clipRect(Rect.fromLTWH(50, 50, 50, 50), clipOp: ui.ClipOp.difference); canvas.drawImageRect( image! , Rect.fromLTWH(0, 0, image! .width.toDouble(), image! .height.toDouble()), Rect.fromLTWH(0, 0, 200, 200), _paint, ); }Copy the code
  • Effect of the sample

The commutation of the Canvas

Canvas provides four transformation operations: displacement, rotation, scaling, and tangent, whose effects are similar to the Transform component provided by the system. Before you get familiar with these four transformation operations, you should first understand the coordinate system in Flutter.

Coordinate system

The coordinate system in a Flutter is the origin of the top left corner of the current canvas, the X-axis is positive to the left, and the Y-axis is positive to the downward. The Angle is in radians, 0 degrees to the right of the horizontal position from the origin, clockwise. PI is horizontal to the left of the origin.

The displacement of the translate

void translate(double dx, double dy)
Copy the code

Move the canvas origin to the specified position, dx and dy can be negative


The zoom scale

void scale(double sx, [double? sy])
Copy the code

For scaling operations,sx and SY are scaling scales in the X and y directions, respectively. If SY is not specified,sx will be used for scaling in both directions. Sx and SY can be negative, and negative values will have the effect of antirotation.

As shown in the figure, black is the original coordinate system, and red is the transformed coordinate system.


Rotate the rotate

void rotate(double radians)
Copy the code

Rotate the canvas with the origin as the center, radians: rotation Angle, radians, that is, PI =180º

As shown in the figure, black is the original coordinate system, and red is the transformed coordinate system.


Oblique skew

void skew(double sx, double sy)
Copy the code

Sx and SY are the tangent values of x and y axes, and the values are the tan values of the trigonometric function, that is, the tangent value of 45 degrees is 1.

As shown in the figure, black is the original coordinate system, and red is the transformed coordinate system.


The save and restore

Saves a copy of the current transform and clip on the save stack.

That is, there is a stack that holds the state of the canvas, and when called save, the current canvas is saved to the stack. When restore is called, the canvas state is removed from the stack. Since it is stored on a stack, the Canvas save and restore have the following features

  • If they come in pairs, there is onesaveYou need to have onerestore
  • Because it is stored in the stack, it has the characteristics of the stack – last in first out, that is, there are multiplesave, the callrestoreWill start from the last onesaveStart.
  • The sample code
@override void paint(Canvas Canvas, Size Size) {// Coordinates _canvasCoordinates(Canvas); DrawRect (Offset. Zero & Size(80, 50), _paint); // Save state 1 canvas.save(); canvas.rotate(pi); canvas.translate(0, 20); canvas.skew(0, 1); _paint.color = Colors.red; canvas.drawRect(Offset.zero & Size(80, 50), _paint); // Restore state 1 canvas.restore(); Canvas. Translate (-100, 0); canvas. Translate (-100, 0); _paint.color = Colors.green; canvas.drawRect(Offset.zero & Size(80, 50), _paint); }Copy the code
  • Effect of the sample


conclusion

Canvas has very powerful functions, and THERE are some APIS that I am vague about, which will be supplemented gradually in the future. The next article will use some common UI requirements in our development to show the powerful functions of Canvas with actual code.