1. Introduction

In this article, we briefly introduce the core rules of Flutter layout for RenderObject, including Constraint down, Size up, and parent node setting the position of this node. In this article, we will examine this rule in detail.

2. Layout principles

The core rules for RenderObject layout are as follows:

  1. A Widget gets a Constraint from its parent node and passes it to the child node.
  2. The Widget lays out its children.
  3. Finally, the node tells its parent its Size.

The Flutter Framework traverses the RenderObject Tree depth-first. The Constraint is passed down through parent to child. To calculate its own Size, a RenderObject must follow the Constraint passed by its parent. For some renderObjects that depend on the Size of their children, you also need to get the Size of their children before calculating their Size. Therefore, the Size of the RenderObject is progressively passed up. A schematic of this rule is shown below:

Next, we’ll examine this core rule through a simple example.

Example 3.

Example code is as follows:

class Example3Test extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(width: 100, height: 100, color: Colors.red), ); }}Copy the code

As you can see, the code is very simple: draw a red square 100 by 100 in the middle of the screen. The diagram is shown below.

The RenderObject Tree for this example is as follows:

Next, we’ll walk you through the constraints and Size passing process in conjunction with the source code.

RenderView’s performLayout function looks like this:

@override
void performLayout() {
  assert(_rootTransform ! =null);
// Set its size to the screen size
  _size = configuration.size;
  assert(_size.isFinite);

// Set the size of the child node to the screen size
  if(child ! =null)
    child.layout(BoxConstraints.tight(_size));
}
Copy the code

As you can see, Size is the screen Size(Size(392.7, 803.6)), and a Constraint(w=392.7, h=803.6) is passed to the child RenderObject using the Layout function. That is, force the Size of its children to be the Size of the screen.

Next, we look at the performLayout function of the child node RenderPositionedBox. The source code is as follows:

void performLayout() {
  final BoxConstraints constraints = this.constraints;
  final boolshrinkWrapWidth = _widthFactor ! =null || constraints.maxWidth == double.infinity;
  final boolshrinkWrapHeight = _heightFactor ! =null || constraints.maxHeight == double.infinity;

  if(child ! =null) {
// Pass constrains to the child node
    child.layout(constraints.loosen(), parentUsesSize: true);
// Use the child node size to calculate their own size
    size = constraints.constrain(Size(shrinkWrapWidth ? child.size.width * (_widthFactor ?? 1.0) : double.infinity,
                                          shrinkWrapHeight ? child.size.height * (_heightFactor ?? 1.0) : double.infinity));
// Set offset in parentData to determine the location of the child node when drawing
    alignChild();
  } else {
    size = constraints.constrain(Size(shrinkWrapWidth ? 0.0 : double.infinity,
                                          shrinkWrapHeight ? 0.0 : double.infinity)); }}Copy the code

Constraints (w=392.7, h=803.6); constraints (0<=w<=392.7, 0<=h<=803.6); It is passed to the child node through the child node’s Layout function.

The child node calculates its Size in its performLayout function, and RenderPositionedBox calculates its own Size based on that Size. PerformLayout for child nodes is analyzed next.

Finally, calculate the position of the child node relative to the RenderPositionedBox based on the Size of the RenderPositionedBox and the Size of the child node, and assign this value to the offset of the child node parentData.

Next, we examine the performLayout function for RenderConstrainedBox. The source code is as follows:

void performLayout() {
  final BoxConstraints constraints = this.constraints;
  if(child ! =null) {
// Pass constrains to the child node
    child.layout(_additionalConstraints.enforce(constraints), parentUsesSize: true);
// Use the child node size to calculate their own size
    size = child.size;
  } else{ size = _additionalConstraints.enforce(constraints).constrain(Size.zero); }}Copy the code

First, the constraints required by the child node are calculated from the Constraint passed by the parent node (constraints) and the Constraint passed by the constructor itself (_additionalConstraints). In this case, Constraint is Constraint(w=100, h=100).

Then, since RenderConstrainedBox is just a container containing child nodes, set its own Size to that of the child node (100, 100).

There is no need to set the parentdata. offset of the child node because the child node is filled with RenderConstrainedBox, so the parentdata. offset of the child node is offset(0,0).

Finally, let’s look at the performLayout function for _RenderColoredBox. RenderColoredBox does not override the performLayout function. The function calls are as follows:

RenderBox’s performResize function is called as follows:

void performResize() {
  // default behavior for subclasses that have sizedByParent = true
  size = constraints.smallest;
  assert(size.isFinite);
}
Copy the code

As you can see, the Constraint passed by the parent calculates its own Size(100, 100).

Constraint is passed from parent to parent in the performLayout function. After the child calls Layout, the child calculates its Size, and the parent calculates its own Size based on the child’s Size to determine its Size. The flow chart is as follows:

4. Summary

This paper analyzes the core rules of Flutter layout based on source code. The key points are as follows:

  • The core layout rules are Constraint down, Size up, and the parent node sets the location of the node.
  • PerformLayout generally includes the following steps: The Constraint is first passed to the child node through the Layout function. The child node calculates its Size from the Layout function in its performLayout function, and then calculates its Size from the Size of the child node. Finally, calculate the Offset of parentData of the child node by its own Size and the Size of the child node. This Offset is used when drawing child nodes.

5. Reference documents

Flutter architectural overview

6. Related articles

Framework of Flutter analysis (a) – architecture overview Flutter analysis framework (2) – Widget framework of Flutter analysis (3) – Element framework of Flutter analysis (4) – RenderObject Flutter analysis framework (5) – widgets, Element, RenderObject tree Flutter framework analysis (7) -relayoutBoundary Flutter framework analysis (8) -Platform Channel Flutter framework analysis – Parent Data Flutter framework analysis -InheritedWidget