preface

Container is a Widget of a Container class in Flutter development, similar to an HTML div. Container is also frequently used in Flutter projects. Then in the process of using, have you ever had the following questions:

  1. What is a Container and how is it implemented?
  2. What are the size constraints of a Container? Do you know why?

The Container is introduced

First let’s see what Contaienr is:The Container is a widget that inherits from the StatelessWidget. It has a variety of properties that allow you to set foreground, background, color, inner and outer margins, cropping, alignment, and so on. And because the Container inherits from the StatelessWidget, it does not build the renderObject and cannot directly contribute to drawing, so take a look at itsbuild()Function implementation:

@override Widget build(BuildContext context) { Widget current = child; if (child == null && (constraints == null || ! {current = LimitedBox(maxWidth: 0.0, maxHeight: 0.0, child: ConstrainedBox(constraints: const BoxConstraints.expand()), ); } if (alignment ! = null) current = Align(alignment: alignment, child: current); final EdgeInsetsGeometry effectivePadding = _paddingIncludingDecoration; if (effectivePadding ! = null) current = Padding(padding: effectivePadding, child: current); if (color ! = null) current = ColoredBox(color: color, child: current); if (clipBehavior ! = Clip.none) { assert(decoration ! = null); current = ClipPath( clipper: _DecorationClipper( textDirection: Directionality.of(context), decoration: decoration ), clipBehavior: clipBehavior, child: current, ); } if (decoration ! = null) current = DecoratedBox(decoration: decoration, child: current); if (foregroundDecoration ! = null) { current = DecoratedBox( decoration: foregroundDecoration, position: DecorationPosition.foreground, child: current, ); } if (constraints ! = null) current = ConstrainedBox(constraints: constraints, child: current); if (margin ! = null) current = Padding(padding: margin, child: current); if (transform ! = null) current = Transform(transform: transform, child: current); return current; }Copy the code

As you can see from the build() function, the Container is just a composite wrapper around the different renderObjectWidget. This simplifies the number of layers of widget nesting in our layout; The “box model” of Flutter has no concept of internal and external margins. Margins are realized by nesting a “box”. We can see the effect of margins because of the order in which the widgets are first nested. The size of this widget is the size of the child plus the “padding” we set. More on size constraints later.

Size and constraints of the process

Before we can learn about size constraints, we need to know the layout calculation flow of the renderObject. Let’s navigate to the RenderObject class, navigate to the function:

abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin implements HitTestTarget { void layout(Constraints constraints, { bool parentUsesSize = false }) { if (! kReleaseMode && debugProfileLayoutsEnabled) Timeline.startSync('$runtimeType', arguments: timelineArgumentsIndicatingLandmarkEvent); . RenderObject? relayoutBoundary; if (! parentUsesSize || sizedByParent || constraints.isTight || parent is! RenderObject) { relayoutBoundary = this; } else { relayoutBoundary = (parent as RenderObject)._relayoutBoundary; }... if (! _needsLayout && constraints == _constraints && relayoutBoundary == _relayoutBoundary) { ... if (! kReleaseMode && debugProfileLayoutsEnabled) Timeline.finishSync(); return; } _constraints = constraints; . if (sizedByParent) { ... try { performResize(); . } catch (e, stack) { ... }... }... try { performLayout(); . } catch (e, stack) { ... }... _needsLayout = false; markNeedsPaint(); if (! kReleaseMode && debugProfileLayoutsEnabled) Timeline.finishSync(); } @protected void performResize(); @protected void performLayout(); void paint(PaintingContext context, Offset offset) { } }Copy the code

Find a layout call:

Okay, let’s break it down:layoutThe input parameter to a function is a constraint that can be retrieved from the outside and then calledperformResizeandperformLayoutWhere the official requirement is that subclasses cannot be overriddenlayoutI’m going to rewrite itperformResizeorperformLayout; The main function is to pass its constraints to subclasses; Get size from subclasses; Continue drawing subclasses, and finally pass the class’spaintFunction to draw. The entire process is recursive, and the size and constraints of each renderObject are calculated and drawn.

Constraint rules

Take a peek at the website of Flutter: