Prelude
I’m familiar with the dictum
"Constraints go down. Sizes go up. Parent sets position.‘
What I’d like to understand is how "constraints" are defined. Is it just a range ("height must be between 30 and infinity"), or is there something more elaborate? Surely I can find the answer from reading Flutter’s code, but I’m not yet at that stage.
A concrete answer addressing a very specific example is much more helpful than hand-waving.
Hence in an effort to avoid vague answers, please address the following concrete example.
Question
Why does uncommenting the commented-out line trigger an error?
import 'package:flutter/material.dart';
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
// crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text('Hello),
Text('World),
]),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('How),
Text('are you?),
]),
]
);
}
}
Clarification
The following is another way of asking the question.
In the three part:
- "Constraints go down."
- "Sizes go up."
- "Parent sets position."
it’s clear what "size" is. It is the Size
class.
It’s also clear what "position" is. There is also a class for that (Offset
).
- Is this the class that captures a "constraint"? Is it visible from the programmer side?
- How does the instance of that class change when we uncomment the commented-out line (thereby triggering the error)?
2
Answers
According to the documentation of the row: https://api.flutter.dev/flutter/widgets/Row-class.html , the first step of the render is
Layout each child a null or zero flex factor (e.g., those that are not Expanded) with unbounded horizontal constraints and the incoming vertical constraints. If the crossAxisAlignment is CrossAxisAlignment.stretch, instead use tight vertical constraints that match the incoming max height.
And the fourth step:
The height of the Row is the maximum height of the children (which will always satisfy the incoming vertical constraints).
Since you’re not specifing any container or "wrapper", Flutter doesn’t know what height ot put on the row when you’re stretching the crossAxis.
In your example:
Let’s say you wrap the first wor in a Container with 400 height and the second text also inside a Container to see what’s happening when stretching to minimun and maximun possible height:
Result:
But if you use another variation: center, start, end, the containers would wrap to its minimum:
Constraints for a widget are defined by 4
doubles
, min and max width, min and max height, with infinity as a possible value.Now on your question, setting
CrossAxisAlignment
toStretch
, causes the constraints passed to the children to betight
in the cross axis.A
tight
constraint in flutter is when, the parent offers to its child only ONE size, for its width and its height, so the min and max width will be equal, same goes for the min and max height.On the other hand, a
loose
constraint only sets the max width and the max height, but the child can be as small as it wants.When the parent gives a constraint, that is
loose
andinfinite
on a specific axis, the child must give back asize
for that axis that is NOTinfinite
, otherwise Flutter will throwBoxConstraints forces an infinite height
.For example, if you have a
Column
, that gives infinite height to its childListView
, theListView
, if not wrapped by a specific widget, will throw, as it’s trying to give back an infinite height to the parent,in that case, you can either wrap the
ListView
with anExpanded
widget, or aSizedBox
.In your example, this is what is happening:
Your
Column
widget, is giving the following height constraints to its 2 children (the 2Rows
)These are
loose
constraints, so the children can take as much size as they need.And each of those
Rows
then, passes the same constraints to its children (the 2Text
widgets), and again, the constraints are the same and loose,so the Text widgets can be as tall as they want.
As soon as you set the
CrossAxisAlignment
toStretch
, the constraints that theRow
is passing down are tight, this means that when the parentColumn
, asks the childRow
that hasStretch
, what size it wants to be,the
Row
will answer withinfinite
(always referring to the height), and this will throw the error, as said before, if the constraint set by the parent is alreadyinfinite
, the size the child wants cannot beinfinite
as well, on that axis.So to solve this, when a child is setting a
tight
constraint for its children, and the parent of that child is givinginfinite
constraint on a specific axis,the child MUST be either wrapped in a
Flex
widget (Expanded
,Flexible
) or it must have afinite
size (e.g wrapping it with aSizedBox
).So for your example, if you wrap your
Stretch
Row, in either aFlexible
orExpanded
widget, your error will go away:To sum up, if a parent Widget gives, on a specific axis,
infinite
contraint, then the child, cannot ask to have its size, on that specific axis,infinite
as well, because, if there are multiple children, the following one won’t have any space left to size themselves.In this case, the child MUST be either wrapped with a
sizing
widget, or with aflex
widget.