preface

In the previous chapters we have basically covered all the basics of Flutter. Readers who have read this column by now should have the ability to draw common Flutter development scenarios and various basic UI components. However, in daily development, business logic varies widely and scenarios alternate. However, Flutter, like the Native platform, allows developers to customize their views based on different scenarios. Make use of the API provided by the Flutter platform to create highly customizable views for complex UI rendering.

Course objectives

  • Understand and master the principle of Flutter custom view
  • Master the method and operation process of Flutter custom view
  • Use the knowledge learned in this section to complete a custom view of a circle that automatically changes color

Automatically change the color of the circle custom view renderings

1. Principle of Flutter custom View

Custom View as the name implies is in accordance with their own intentions and ideas to achieve their own UI View, familiar with Android development partners know that in the Android platform if you want to customize the View, need to inherit View or ViewGroup, and then rewrite the onDraw method, In onDraw, paint the corresponding content on the Canvas. To trigger a redraw, you need to refresh the view each time by calling the Invalidate notification. In a Flutter, if a developer wants to perform a custom view operation, he/she needs to inherit the CustomPainter and draw the corresponding content in his/her paint method. ShouldRepaint returns the value to determine if it needs to be redrawn.

Analogy analysis

In fact, as for the custom view, we can completely simulate the painting operation in real life. We draw the expected graphics or content on a canvas or rice paper with a brush, but the object operated here is changed in form. The brush in real life is the same as the Panit in Flutter. The canvas is equivalent to the canvas in a Flutter. The content of the paint is the same as the content we want to draw on the Flutter view layer. This can be done by setting different properties to the paint, such as color value, thickness, anti-aliasing, etc.

1.1 Paint: Paint

Paint is the base we use to draw custom views. We can configure Paint properties to draw different styles of content by setting Paint Settings. Common attributes are

  • Color (sets the brush color)
  • StrokeCap (brush stroke type)
  • ColorFilter (Color rendering mode, usually changed by matrix effects, but only color blending mode can be used in Flutter)
  • Style (Stroke or fill corresponds to paintingstyle. stroke and paintingstyle. fill)
  • StrokeWidth (brush width)
  • IsAntiAlias (anti-aliasing or not)
  • Shader (LinearGradient, SweepGradient scan gradient, RadialGradient RadialGradient)

I won’t list the other attributes, but interested readers can write their own code to verify the effect

1.2 Canvas: Canvas

With the brushes configured in the first step, we can now draw concrete shapes on the canvas using the brushes configured in the first step. There are a series of drawing methods available for Flutter, such as drawCircle to draw a circle and drawRect to draw a rectangle. DrawPath Draws any path. Canvas provides most of the commonly used drawing methods, which are preceded by Canvas. drawxxx(XXX,paint).

Canvas not only provides a large number of methods related to drawing, but also commonly used methods such as translation canvas.translate, rotation canvas.rotate and clipping canvas.clipRect.

Note: During the whole drawing process of canvas, the system coordinate system is located at the upper left corner, and the x and Y axes are positive to the right and down respectively

1.3 Draw specific Content

Now that we have paint and Canvas ready, we just need to implement the logic in the custom View paint method to do the painting. Example code for drawing a triangle using path and a circle using drawCircle looks like this:

  @override
  void paint(Canvas canvas, Size size) {
    // Use path to draw triangles
    Path path = Path();
    path.lineTo(100.0);
    path.lineTo(0.100);
    path.close();
    canvas.drawPath(path, _paint);

    // Use drawCircle (100, 100) with radius 50
    // canvas.drawCircle(Offset(100, 100), 50, _paint);
  }
Copy the code

triangle

A solid circle with center coordinates of (100, 100) and radius of 100

1.4 Troubleshooting Whether the view needs to be refreshed

If the view doesn’t need to change, or if the image doesn’t need to change during its lifetime, we should simply return false in shouldRepaint to indicate that the current view doesn’t need to be refreshed.


  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return false;
  }
Copy the code

Otherwise, you need to change the return value of shouldRepaint to tell the view whether it needs to be refreshed or not, depending on the business scenario.

The complete code is as follows;

import 'package:flutter/material.dart';

/** * desc: Customizable view * author: xiedong * date: 2021/9/2 **/

void main(a) {
  runApp(MaterialApp(
    home: CustomTriangleView(),
  ));
}

class CustomTriangleView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Custom VIEW"), ), body: CustomPaint( painter: TriangleView(), ), ); }}class TriangleView extends CustomPainter {
  var_paint; TriangleView() { _paint = Paint() .. color = Colors.blueAccent// Brush color
      ..strokeCap = StrokeCap.round // Brush stroke type
      ..isAntiAlias = true // Whether to enable anti-aliasing
      ..blendMode = BlendMode.exclusion // Color blending mode
      ..style = PaintingStyle.fill // Paint style, default is fill
      ..colorFilter = ColorFilter.mode(Colors.blueAccent,
          BlendMode.exclusion) Color rendering mode is usually changed by matrix effects, but only color blending mode can be used in Flutter
      ..maskFilter = MaskFilter.blur(BlurStyle.inner, 3.0) // Blur mask effect, which is the only one in Flutter
      ..filterQuality = FilterQuality.high // Color rendering mode quality
      ..strokeWidth = 15.0; // The brush width
  }

  @override
  void paint(Canvas canvas, Size size) {
    // Use path to draw triangles
    // Path path = Path();
    // path.lineTo(100, 0);
    // path.lineTo(0, 100);
    // path.close();
    // canvas.drawPath(path, _paint);

    // Use drawCircle (100, 100) with radius 50
    canvas.drawCircle(Offset(100.100), 50, _paint);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return false; }}Copy the code

2. Draw circles that automatically change color

At the beginning of this chapter, we put forward the course requirements of drawing a circle whose color can change automatically by using the knowledge we have learned. ShouldRepaint should return true because the state of the custom VIEW needs to change dynamically

2.1. Set shouldRepaint to return true


  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }
Copy the code

The paint function remains the same. In the paint function, we use the logic that drew the circle in Part 1 above. The change is that we need to use state to dynamically change the paint color value.

2.2 Draw the function as follows:

@override
 void paint(Canvas canvas, Size size) {
   // Use path to draw triangles
   // Path path = Path();
   // path.lineTo(100, 0);
   // path.lineTo(0, 100);
   // path.close();
   // canvas.drawPath(path, _paint);

   // Use drawCircle (100, 100) with radius 50
   print('---------- image redrawn ');
   canvas.drawCircle(Offset(100.100), 50, _paint);
 }
Copy the code

2.3 Use Flutter periodic functions to dynamically modify the color values in the incoming custom View

 @override
  void initState(a) {
    super.initState();
    int count = 0;
    var period = Duration(seconds: 1);
    // print('currentTime='+DateTime.now().toString());
    Timer.periodic(period, (timer) {
      print('---- color value changes --');
      this.setState(() {
        _color = _colorArr[Random().nextInt(4)];
      });
    });
  }
  
Copy the code

2.4 Receive color values passed by state in the constructor of a custom View

In the constructor of the custom view, initialize the paint color value from the fixed value in the first part, change the variable value passed back from state, and refresh the entire view with the state change.

AutoChangeColorCircle(_color) { _paint = Paint() .. color = _color// Brush color
      ..strokeCap = StrokeCap.round // Brush stroke type
      ..isAntiAlias = true // Whether to enable anti-aliasing
      ..blendMode = BlendMode.exclusion // Color blending mode
      ..style = PaintingStyle.fill // Paint style, default is fill
      ..colorFilter = ColorFilter.mode(Colors.blueAccent,
          BlendMode.exclusion) Color rendering mode is usually changed by matrix effects, but only color blending mode can be used in Flutter
      ..maskFilter = MaskFilter.blur(BlurStyle.inner, 3.0) // Blur mask effect, which is the only one in Flutter
      ..filterQuality = FilterQuality.high // Color rendering mode quality
      ..strokeWidth = 15.0; // The brush width
  }
Copy the code

Results the following

The complete code is as follows:


import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';

/** * desc: Customizable view * author: xiedong * date: 2021/9/2 **/

void main(a) {
  runApp(MaterialApp(
    // home: CustomTriangleView(),
    home: AutoChangeColorCircleView(),
  ));
}




class AutoChangeColorCircleView extends StatefulWidget {
  @override
  State<StatefulWidget> createState(a) => ViewState();
}

class ViewState extends State<AutoChangeColorCircleView> {
  var _colorArr = [
    Colors.amberAccent,
    Colors.blue,
    Colors.deepOrange,
    Colors.cyan,
    Colors.black,
    Colors.deepPurple,
  ];
  var _color;

  @override
  void initState(a) {
    super.initState();
    int count = 0;
    var period = Duration(seconds: 1);
    // print('currentTime='+DateTime.now().toString());
    Timer.periodic(period, (timer) {
      print('---- color value changes --');
      this.setState(() {
        _color = _colorArr[Random().nextInt(4)];
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Custom VIEW"), ), body: CustomPaint( painter: AutoChangeColorCircle(_color), ), ); }}class AutoChangeColorCircle extends CustomPainter {
  var_paint; AutoChangeColorCircle(_color) { _paint = Paint() .. color = _color// Brush color
      ..strokeCap = StrokeCap.round // Brush stroke type
      ..isAntiAlias = true // Whether to enable anti-aliasing
      ..blendMode = BlendMode.exclusion // Color blending mode
      ..style = PaintingStyle.fill // Paint style, default is fill
      ..colorFilter = ColorFilter.mode(Colors.blueAccent,
          BlendMode.exclusion) Color rendering mode is usually changed by matrix effects, but only color blending mode can be used in Flutter
      ..maskFilter = MaskFilter.blur(BlurStyle.inner, 3.0) // Blur mask effect, which is the only one in Flutter
      ..filterQuality = FilterQuality.high // Color rendering mode quality
      ..strokeWidth = 15.0; // The brush width
  }

  @override
  void paint(Canvas canvas, Size size) {
    // Use path to draw triangles
    // Path path = Path();
    // path.lineTo(100, 0);
    // path.lineTo(0, 100);
    // path.close();
    // canvas.drawPath(path, _paint);

    // Use drawCircle (100, 100) with radius 50
    print('---------- image redrawn ');
    canvas.drawCircle(Offset(100.100), 50, _paint);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true; }}Copy the code

See the Github Entry to Flutter for the full code

In addition to inheriting the CustomPainter to redraw graphics, we can also create custom views by combining existing components of Flutter in some simple scenarios. For example, in my last post, we talked about an advanced journey to Flutter – a class View on Flutter is a way to create a custom View by combining existing Flutter components. Interested readers can find this article in my column. Compare the advantages and disadvantages of customizing Flutter in two different ways.