Preface:

This is the 22nd day of my participation in the August More Text Challenge. In preparation for the Nuggets’ August challenge, I’m going to pick 31 components this month that I haven’t covered before for a full analysis and attribute presentation. These articles will serve as important material for future compilation of Flutter components. I hope I can stick to it, your support will be my biggest motivation ~

This series Component articles The list of
1.NotificationListener 2.Dismissible 3.Switch
4.Scrollbar 5.ClipPath 6.CupertinoActivityIndicator
7.Opacity 8.FadeTransition 9. AnimatedOpacity
10. FadeInImage 11. Offstage 12. TickerMode
13. Visibility 14. Padding 15. AnimatedContainer
16.CircleAvatar 17.PhysicalShape 18.Divider
Flexible, Expanded, and Spacer 20.Card 21.SizedBox
22.ConstrainedBox [this article]

Understand ConstrainedBox

ConstrainedBox imposes additional constraints on its child components. So what are constraints? Why extra? What is the role of constraints? Take a look through this article.


ConstrainedBox basic information

Below is ConstrainedBox component class definition and construction method, it can be seen that it inherited from SingleChildRenderObjectWidget. You can take a child component that must be constructed with a constraints parameter of type BoxConstraints.

/// The additional constraints to impose on the child.
final BoxConstraints constraints;
Copy the code

2. Recognize the BoxConstraints

The BoxConstraints class is an abstraction of range and maintains four values: these four values form a range of dimensions that define the size of the child component. Once again, the component itself has no concept of size. By component size, I mean the size of the render object it maintains.

Members of the object Object type The default value introduce
minWidth double 0 Minimum size width
maxWidth double double.infinity Maximum dimension width
minHeight double 0 Size height minimum
maxHeight double double.infinity Maximum dimension height


BoxConstraints, on the other hand, is a property of the RenderObject that takes care of its own region constraints and passes them down with additional constraints. That is, each RenderObject has constraint properties. For example, here is a 100*50 SizedBox, painted blue with ColoredBox.

SizedBox(
  width: 100,
  height: 50,
  child: ColoredBox(
    color: Colors.blue.withAlpha(88))),Copy the code

As you can see, the SizedBox component maintains the RenderConstrainedBox rendering object, which has its own constraints imposed by the upper layer [w(0,800) -h (0,600)], that is, the render object size needs to be within this range. In addition, it imposes an additional [w(100,100) -h (50,50)] constraint on the child nodes. Rendercoloredbox (w(100,100) -h (50,50))); render ColoredBox (w(100,100) -h (50,50))

This should give you a simple idea of what the constraints are.


3. The use of ConstrainedBox

Let’s take a look at ConstrainedBox in action with a simple example. Based on the above case, the width is limited within [10~40]. As you can see, the SizedBox has to be between 10 and 40 widths because of the constraints imposed by ConstrainedBox here, even though the child component uses SizedBox to explicitly say that it wants a size of 100*50.

ConstrainedBox(
  constraints: BoxConstraints(
    minWidth: 10,
    maxWidth: 40,
  ),
  child:
    SizedBox(
      width: 100,
      height: 50,
      child: ColoredBox(
        color: Colors.blue.withAlpha(88))));Copy the code

As shown below, the render object corresponding to the SizedBox has its own constraint of [w(10,40) -h (0,600)] and additional constraint of [w(100,100) -h (50,50)] to the child node. These two constraints will combine to become the constraint of the next child node.


Second, learn more about BoxConstraints

Simply put, BoxConstraints maintains four values, but the objects derived from these four values are worth teasing out.

Constructor of BoxConstraints

There are six constructors for BoxConstraints, with the normal constructor passing in four values, as you saw earlier.


The. Tight construct, which requires passing in a Size object that sets the constraint area to the specified width and height, is the same as SizedBox.


. TightFor construct, the width and height value passed in. Unlike tight, where width/height is empty, if width is empty, width ranges from 0 to infinity, and height is similar. In our previous SizedBox article, we said that inside a SizedBox, we use.Tightfor to construct constraint objects based on width and height. That is, you use tightFor to create constraints that are used in ConstrainedBox, essentially the same as SizedBox.


Expand width and height. Similar to tightFor, the width/height can be empty. The difference is that: if the width is empty, the width value range is between infinity and infinity, that is to say, the area is infinite and will be expanded as much as possible.


The.loose construct, similar to the.tight construct, requires passing in a Size object. The lower bound is 0, and the upper bound is the width and height of size. So this constraint is loose, unlike.Tight which fixes width and height.


. TightForFinite construction, default width and height unlimited. Take the width example. By default width is infinite, ranging from 0 to infinity. If passed width is not infinite, the width is fixed to width, and the height is similar.


2. Member methods of BoxConstraints

There are many member methods for BoxConstraints, but they all operate around these four property values. Here are some of the most important ones. First, constrainWidth. You can see that you need to pass in a width argument, which is infinite by default and returns using the num. Clamp function.

double constrainWidth([ double width = double.infinity ]) {
   assert(debugAssertIsValid());
   return width.clamp(minWidth, maxWidth);
}
Copy the code

This is a function that you probably don’t use very often, so let’s test it out with a method. It can be seen that clamp’s parameters are upper and lower bound: if less than the lower bound, the lower bound value is returned; if greater than the upper bound, the upper bound value is returned. Returns this value if the upper and lower bounds are direct. That is, a return value does not exceed the upper and lower bounds of the CLAMP parameter. This approach is very much in line with the idea of constraints.

main(){
  int a  = 10.clamp(3.6); / / 6
  int b  = 4.clamp(3.6); / / 4
  int c  = 1.clamp(3.6); / / 3
  print('$a.$b.$c');
}
Copy the code

ConstrainHeight is similar.

double constrainHeight([ double height = double.infinity ]) {
  assert(debugAssertIsValid());
  return height.clamp(minHeight, maxHeight);
}
Copy the code

The Enforce method is a very important method that can combine two constraints to create a new one. As you can see, the four values of the constraint are calculated by clamp to get the new values. This ensures that this constraint takes precedence over the input constraint. Think about this constraint overlay in light of the example above.

BoxConstraints enforce(BoxConstraints constraints) {
  return BoxConstraints(
    minWidth: minWidth.clamp(constraints.minWidth, constraints.maxWidth),
    maxWidth: maxWidth.clamp(constraints.minWidth, constraints.maxWidth),
    minHeight: minHeight.clamp(constraints.minHeight, constraints.maxHeight),
    maxHeight: maxHeight.clamp(constraints.minHeight, constraints.maxHeight),
  );
}
Copy the code

Constrain method; the input is a Size object and returns a Size object. That is, a new size is returned with the constraint and a size. ConstrainWidth and constrainHeight, as you can see from the code below, are simply operations on the original size. That is to say, the new size is based on the input size, width and height as far as possible in line with the input size.

Size constrain(Size size) {
  Size result = Size(constrainWidth(size.width), constrainHeight(size.height));
  assert(() {
    result = _debugPropagateDebugSize(size, result);
    return true; } ());return result;
}
Copy the code

ConstrainedBox source code interpretation

ConstrainedBox inherited from SingleChildRenderObjectWidget, need to maintain a rendering object.


The render object that implements constraints is the RenderConstrainedBox, which passes in constraints at construction time.


RenderConstrainedBox layout in performLayout, when the Child is not empty, the child renders the layout of the object first. And superimpose _additionalConstraints and its own constraints using the Enforce method as constraints for the child component. After the child component layout is played, its own size is equal to the size of the Child render object.

This is how the parent renders constraints upward to the child, and the child renders dimensions upward to the parent. By now you should have a deeper understanding of layout constraints, so that’s the end of this article, thanks for watching, see you tomorrow ~