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.
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?),
]),
]
);
}
}
The following is another way of asking the question.
In the three part:
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
).
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
to Stretch
, causes the constraints passed to the children to be tight
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
and infinite
on a specific axis, the child must give back a size
for that axis that is NOT infinite
, otherwise Flutter will throw BoxConstraints forces an infinite height
.
For example, if you have a Column
, that gives infinite height to its child ListView
, the ListView
, 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 an Expanded
widget, or a SizedBox
.
In your example, this is what is happening:
Your Column
widget, is giving the following height constraints to its 2 children (the 2 Rows
)
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 2 Text
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
to Stretch
, the constraints that the Row
is passing down are tight, this means that when the parent Column
, asks the child Row
that has Stretch
, what size it wants to be,
the Row
will answer with infinite
(always referring to the height), and this will throw the error, as said before, if the constraint set by the parent is already infinite
, the size the child wants cannot be infinite
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 giving infinite
constraint on a specific axis,
the child MUST be either wrapped in a Flex
widget (Expanded
, Flexible
) or it must have a finite
size (e.g wrapping it with a SizedBox
).
So for your example, if you wrap your Stretch
Row, in either a Flexible
or Expanded
widget, your error will go away:
return Column(
children: [
Flexible( /// this fixes your issue
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text('Hello'),
Text('World'),
]),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('How'),
Text('are you?'),
]),
]
);
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 a flex
widget.