Note: To make analysis simpler and logical, we have removed some of the source code and comments, leaving only the main code and logic. If you haven’t read the previous article, please click the link below. Flutter must understand the relationships between Widgets, Elements and RenderObjects

Summary of Element

UpdateChild method

Following up on the previous article, which mentioned this method is more important, we will have a separate chapter to explain it. Here is the source code for updateChild().

abstract class Element extends DiagnosticableTree implements BuildContext {
  Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
      if (newWidget == null) {
        if(child ! =null)
          deactivateChild(child);/ / comment 1
        return null;
      }
      if(child ! =null) {
        if (child.widget == newWidget) {
          if(child.slot ! = newSlot) updateSlotForChild(child, newSlot);/ / comment 2
          return child;
        }
        if (Widget.canUpdate(child.widget, newWidget)) {
          if(child.slot ! = newSlot) updateSlotForChild(child, newSlot); child.update(newWidget);/ / comment 3
          return child;
        }
        deactivateChild(child);
      }
      return inflateWidget(newWidget, newSlot);/ / comment 4}}Copy the code

This method updates the configuration of an Element to a function that removes data from the tree.

  • Note 1

    DeactivateChild () deletes Element from the Element tree,

With this analysis, let’s start with a short paragraph, and now finish with Element.

RenderObject overview

RenderObject definition

Again, let’s start with the concept. Here’s the concept of ReaderObject.

An object in the render tree.

The concept is simple and basically means rendering an object in a tree. Each RenderObject hangs on a rendering tree. Let’s look at the source code for RenderObject.

abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin implements HitTestTarget {
  ParentData parentData;
  Constraints _constraints;
  void layout(Constraints constraints, { bool parentUsesSize = false{}})void paint(PaintingContext context, Offset offset) { }
  
  void performLayout();
  void markNeedsPaint() {
  }
}
Copy the code

We provide these methods together so that you can see that the main responsibility of the RenderObject is to draw and lay out, that is to say, this is a real RenderObject, so let’s look at how this object is laid out and rendered.

If we want to draw a red square on the screen, two important questions need to be solved, the first is, where do we draw it? The second is, how do you draw it? Where to draw is the layout, how to draw is the rendering, the following we analyze the two problems, starting from the layout.

RenderObject layout

We’ll start with Layout, which calculates the size and layout of the render object. This method is usually not overridden by classes. If you want to override the layout, override performLayout().

  abstract class RenderObject {
    void layout(Constraints constraints, { bool parentUsesSize = false }) {
      
      RenderObject relayoutBoundary;/ / comment 1
      if(! parentUsesSize || sizedByParent || constraints.isTight || parentis! RenderObject) {/ / comment 2
        relayoutBoundary = this;/ / comment 3
      } else {
        final RenderObject parent = this.parent;
        relayoutBoundary = parent._relayoutBoundary;/ / comment 4
      }
       _relayoutBoundary = relayoutBoundary;
      if (sizedByParent) {
          performResize();/ / comment 5
      }
      performLayout();/ / comment 6markNeedsPaint(); }}Copy the code

Above is the layout method. This method takes two parameters. The first parameter, constraints, is passed in through the parent class.

  • Note 1

    RelayoutBoundary is an important variable and we plan to use a summary to explain it.

RelayoutBoundary

We have declared a property relayoutBoundary in comment 1. The property is called layout boundary. This property is used to improve rendering efficiency (because this property is used in both layout and rendering). Now if the layout of a leaf RenderObject changes, this must cause the parent layout of the leaf node to be rearranged. This must cause inefficiency. This property is used by Flutter to prevent the parent from being rearranged.

  • parentUsesSize

    The second argument to layout, the parent control layout is dependent on the child control layout. The default value is false. The default parent control does not depend on the child control layout.

  • sizedByParent

    The size of the child control is entirely within the constraints of the parent control, that is, between the min and Max of the parent control.

  • constraints.isTight

    So min is equal to Max, which is a little bit easier to understand.

  • parent is! RenderObject

    This condition is easy to understand: parent is not RenderObject.

If one of the four conditions above is true, execute the code in comment 3, which means the layout boundary is itself, because the layout change of this node does not cause the parent node to be rearranged. Otherwise, execute comment 4 and assign the parent’s layout boundaries to yourself.

  • Note 5

PerformResize () is a subclass of the performResize() method. The main function of this method is to update the size of the render object. Of course, this method is called if sizedByParent is true.

  • Note 6

The performLayout() method, which also requires subclasses, calculates the layout of the RenderObject.

RenderConstrainedBox: RenderConstrainedBox: RenderConstrainedBox: RenderConstrainedBox: RenderConstrainedBox: RenderConstrainedBox: RenderConstrainedBox: RenderConstrainedBox

class RenderConstrainedBox extends RenderProxyBox {}class RenderProxyBox extends RenderBox {}abstract class RenderBox extends RenderObject {}Copy the code

RenderConstrainedBox obviously inherits RenderObject from the code above, so let’s look at the implementation of the performLayout() method.

class RenderConstrainedBox extends RenderProxyBox {
	  @override
    void performLayout() {
      if(child ! =null) {
        child.layout(_additionalConstraints.enforce(constraints), parentUsesSize: true)
        size = child.size;
      } else{ size = _additionalConstraints.enforce(constraints).constrain(Size.zero); }}}Copy the code

RenderConstrainedBox performLayout() is called when RenderObject’s Layout() method is called, which in turn calls Child.Layout () until the layout is complete. Constraints are passed from parent class to subclass, but size is passed from subclass to parent class. This is much easier to visualize in a diagram.

RenderObject rendering

The main function rendered in RenderObject is paint(). There is no implementation in RenderObject, so you need to subclass it. Here is the paint method.

void pait(PaintingContext context, Offset offset) { }
Copy the code

The above paint has two parameters. Explain the two parameters so that you can see what the function means.

  • context

    Context is PaintingContext, and this kind of inherits from ClipContext, and in ClipContext you have a Canvas, so it kind of makes sense, that Context is a Canvas that you draw things on.

  • offset

    Offset is offset, which is a position on the screen coordinates.

So we should be able to guess that paint() is just a way of drawing a context onto a screen location, and here you might have a question, let’s say we want to draw a red square, and now we only have the canvas and draw it to that location, and they don’t tell me the size of the red square, how do I draw it? In the previous section (RenderObject Layout), we looked at the function performResize(), which already calculates the size of the red square.

Other recommendations

  • Detailed implementation and principle analysis of Flutter startup page (splash screen page)
  • Flutter must understand the relationships between Widgets, Elements and RenderObjects
  • Flutter startup process and principle analysis

reference

  • The principle of Flutter and its practice
  • Dealing with box constraints
  • Flutter code
  • The Layer Cake