Flutter Advanced - Layout (Layout) Principles

1. Constraints, size, location  

  @override
  Widget build(BuildContext context) {
    return Scaffold(body: LayoutBuilder(builder: (context, constraints) {
      print("body约束:" + constraints.toString());
      return Container(
          color: Colors.black,
          width: 300,
          height: 300,
          child: LayoutBuilder(builder: (context, constraints) {
            print("Container约束:" + constraints.toString());
            return Container(width: 20, height: 20, color: Colors.orange);
          }));
    }));
  }

Running effect diagram: 

Console output:

flutter: body constraints: BoxConstraints(0.0<=w<=390.0, 0.0<=h<=844.0)
flutter: Container constraints: BoxConstraints(w=300.0, h=300.0)

From this it can be concluded that:

  • The body is a loose constraint on the child control (the child control can be set to specify the width and height);
  • Container is a tight constraint on the child control (if the child control does not specify how to place it, because if the position is not specified, the system does not know how to place the child control after zooming in or out, it simply fills the parent control, setting the width and height is natural will fail), in essence, the child control violates the constraints of the parent control, so it will be executed according to the constraints of the parent control
  • Summary: When setting the size of the child control, if you can't control it, you can use LayoutBuilder(builder: (context, constraints){}) to print it to see if it is tight or loose

Layout principle:

The layout process of each component before rendering can be divided into two linear processes. Layout constraints are first passed down from the top of the component, and layout information is passed up from the bottom.

To put it simply, flutter will traverse the component tree when laying out, starting from the root (deep first depth first principle) to pass constraints downward, and when the child control constraints violate the parent control constraints, the parent control constraints will be executed. The layout of flutter is that onePass only needs to traverse the WidgetTree once, pass the size upwards, and finally the parent gets the size and decides where to put the children

These two linear processes will be completed in the RenderObject tree referenced by the element tree, and the final layout information will be saved in the RenderObject. Therefore, when rebuilding components, if elements and RenderObjects can be reused, the same layout information as last time can also be used. This one-way way of passing and saving information is one of the important reasons why Flutter's layout performance is better than other frameworks.

The RenderObject tree is composed of RenderObjects one by one. When the Element instance is mounted on the element tree, the component's createRenderObject() method will be called to generate the corresponding RenderObject. Since the RenderObject tree is referenced by the element tree, and its main task is to help the Element instance do specific rendering work, the RenderObject tree is also often called the subtree of the element tree.

 Each RenderObject will be held by the element and will be reused as much as possible after the component is rebuilt. Whenever the state of the element changes, the updateRenderObject() method of the component will be called to update the render object, and the value on the screen will eventually be updated.

2. Custom box layout constraints

 @override
  Widget build(BuildContext context) {
    return Scaffold(body: LayoutBuilder(builder: (context, constraints) {
      print("body约束:" + constraints.toString());
      return Container(
        constraints: BoxConstraints(
            minWidth: 60, minHeight: 60, maxWidth: 100, maxHeight: 100),
        child: LayoutBuilder(builder: (context, constraints) {
          print("Container约束:" + constraints.toString());
          return FlutterLogo(size: 500);
        }),
      );
    }));
  }

flutter: body约束:BoxConstraints(0.0<=w<=390.0, 0.0<=h<=844.0)
flutter: Container约束:BoxConstraints(60.0<=w<=100.0, 60.0<=h<=100.0)

3.Stack cascading components

  1. (There are both ordinary Widgets and Position-wrapped Widgets in children), and its size is determined by the largest sub-component in the children that is not wrapped by Position. ( The sub-components wrapped by Position are somewhat similar to absolute in the front-end layout. The layout will be separated from the document flow) can be displayed beyond the parent component, and if you don’t want to display, you can also set clipBehavior: Clip.hardEdge to force the excess part to be clipped.
  2. (All in children are ordinary Widgets), whose size is wrapped around subcomponents
  3. ( The children are all Widgets wrapped by Position, so there is no reference object and I don’t know how to lay them out.) The size of the Stack will be filled with the parent component as large as possible, ( because only the Stack itself is large enough and the corresponding children set the upper left alignment or other alignment. Make sense, otherwise they will be squeezed together and lose the meaning of the original setting alignment )

4、CustomMultiChildLayout

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: CustomMultiChildLayout(
      delegate: MyDelegate(4),
      children: [
        LayoutId(
            id: "text",
            child: Text(
                "测试文字下划线测试文字下划线测试文字下划线测试文字下划线测试文字下划线测试文字下划线测试文字下划线测试文字下划线")),
        LayoutId(
            id: "underline",
            child: Container(
              color: Colors.green,
            ))
      ],
    ));
  }

class MyDelegate extends MultiChildLayoutDelegate {

final double thickness;

MyDelegate(this.thickness);



@override

Size getSize(BoxConstraints constraints) {

return super.getSize(constraints);

}

@override

void performLayout(Size size) {

final sizeText = layoutChild("text", BoxConstraints.loose(size));



final sizeUnderline =

layoutChild("underline", BoxConstraints.tight(Size(sizeText.width, thickness)));



final left = (size.width - sizeText.width) / 2;

final top = (size.height - sizeText.height) / 2;

positionChild("text", Offset(left, top));

positionChild("underline", Offset(left, top + sizeText.height));

}



@override

bool shouldRelayout(covariant MultiChildLayoutDelegate oldDelegate) => true;

}

 5、RenderObject

 @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Container(
            color: Colors.red,
            child: ShadowBox(child: FlutterLogo(size: 200), distance: 200)));
  }


//SingleChildRenderObjectWidget一种能真正画到屏幕上的widget
class ShadowBox extends SingleChildRenderObjectWidget {
  double distance;
  ShadowBox({Widget child, this.distance}) : super(child: child);

  @override
  RenderObject createRenderObject(BuildContext context) {
    return RenderShadowBox(distance);
  }

  @override
  void updateRenderObject(
      BuildContext context, covariant RenderShadowBox renderObject) {
    renderObject.distance = distance;
  }
}

//RenderObjectWithChildMixin比较方便的设置一个child
class RenderShadowBox extends RenderProxyBox with DebugOverflowIndicatorMixin {
  double distance;

  RenderShadowBox(this.distance);

  //处理布局
  // @override
  // void performLayout() {
  //   child.layout(constraints, parentUsesSize: true); //parentUsesSize: false
  //   // 如果parentUsesSize: false则parent的size跟这个child没关系以后再relayout凡是涉及到这个child的不会再更新,如果是true则代表会使用这个child
  //   size = (child as RenderBox).size; // relayout boundary
  //   // size = Size(300, 100);
  // }

  //处理绘制
  //其实layout和paint各遍历一次
  @override
  void paint(PaintingContext context, Offset offset) {
    context.paintChild(child, offset);
    context.canvas.clipRRect(RRect.fromLTRBAndCorners(105, 5, 5, 5));
    //建立新图层,可以在新图层上做各种操作
    context.pushOpacity(offset, 100, (context, offset) {
      context.paintChild(child, offset + Offset(distance, distance));
    });
    //containerRect: 能画的范围
    //childRect: 想让画的范围
    //警戒线
    paintOverflowIndicator(
        context,
        offset,
        Offset.zero & size, //Rect.fromLTWH(0, 0, size.width, size.height)
        Offset.zero & child.size);
  }
}

Guess you like

Origin blog.csdn.net/RreamigOfGirls/article/details/130989686