layout method
void
layout(
- Context context,
- BoxConstraints constraints, {
- bool parentUsesSize = false,
override
First widget pass to calculate the children layout and bounding box
Implementation
@override
void layout(
Context context,
BoxConstraints constraints, {
bool parentUsesSize = false,
}) {
// Determine used flex factor, size inflexible items, calculate free space.
var totalFlex = 0;
Widget? lastFlexChild;
final maxMainSize = direction == Axis.horizontal
? constraints.maxWidth
: constraints.maxHeight;
final canFlex = maxMainSize < double.infinity;
var crossSize = 0.0;
var allocatedSize = 0.0; // Sum of the sizes of the non-flexible children.
var index = _context.firstChild;
for (final child in children.sublist(_context.firstChild)) {
final flex = child is Flexible ? child.flex : 0;
final fit = child is Flexible ? child.fit : FlexFit.loose;
if (flex > 0) {
assert(() {
final dimension = direction == Axis.horizontal ? 'width' : 'height';
if (!canFlex &&
(mainAxisSize == MainAxisSize.max || fit == FlexFit.tight)) {
throw Exception(
'Flex children have non-zero flex but incoming $dimension constraints are unbounded.',
);
} else {
return true;
}
}());
totalFlex += flex;
} else {
BoxConstraints? innerConstraints;
if (crossAxisAlignment == CrossAxisAlignment.stretch) {
switch (direction) {
case Axis.horizontal:
innerConstraints = BoxConstraints(
minHeight: constraints.maxHeight,
maxHeight: constraints.maxHeight,
);
break;
case Axis.vertical:
innerConstraints = BoxConstraints(
minWidth: constraints.maxWidth,
maxWidth: constraints.maxWidth,
);
break;
}
} else {
switch (direction) {
case Axis.horizontal:
innerConstraints = BoxConstraints(
maxHeight: constraints.maxHeight,
);
break;
case Axis.vertical:
innerConstraints = BoxConstraints(maxWidth: constraints.maxWidth);
break;
}
}
child.layout(context, innerConstraints, parentUsesSize: true);
assert(child.box != null);
allocatedSize += _getMainSize(child);
crossSize = math.max(crossSize, _getCrossSize(child));
if (direction == Axis.vertical &&
allocatedSize > constraints.maxHeight) {
break;
}
}
lastFlexChild = child;
index++;
}
_context.lastChild = index;
final totalChildren = _context.lastChild - _context.firstChild;
// Distribute free space to flexible children, and determine baseline.
final freeSpace = math.max(
0.0,
(canFlex ? maxMainSize : 0.0) - allocatedSize,
);
var allocatedFlexSpace = 0.0;
if (totalFlex > 0) {
final spacePerFlex = canFlex && totalFlex > 0
? (freeSpace / totalFlex)
: double.nan;
for (final child in children) {
final flex = child is Flexible ? child.flex : 0;
final fit = child is Flexible ? child.fit : FlexFit.loose;
if (flex > 0) {
final maxChildExtent = canFlex
? (child == lastFlexChild
? (freeSpace - allocatedFlexSpace)
: spacePerFlex * flex)
: double.infinity;
double? minChildExtent;
switch (fit) {
case FlexFit.tight:
assert(maxChildExtent < double.infinity);
minChildExtent = maxChildExtent;
break;
case FlexFit.loose:
minChildExtent = 0.0;
break;
}
BoxConstraints? innerConstraints;
if (crossAxisAlignment == CrossAxisAlignment.stretch) {
switch (direction) {
case Axis.horizontal:
innerConstraints = BoxConstraints(
minWidth: minChildExtent,
maxWidth: maxChildExtent,
minHeight: constraints.maxHeight,
maxHeight: constraints.maxHeight,
);
break;
case Axis.vertical:
innerConstraints = BoxConstraints(
minWidth: constraints.maxWidth,
maxWidth: constraints.maxWidth,
minHeight: minChildExtent,
maxHeight: maxChildExtent,
);
break;
}
} else {
switch (direction) {
case Axis.horizontal:
innerConstraints = BoxConstraints(
minWidth: minChildExtent,
maxWidth: maxChildExtent,
maxHeight: constraints.maxHeight,
);
break;
case Axis.vertical:
innerConstraints = BoxConstraints(
maxWidth: constraints.maxWidth,
minHeight: minChildExtent,
maxHeight: maxChildExtent,
);
break;
}
}
child.layout(context, innerConstraints, parentUsesSize: true);
assert(child.box != null);
final childSize = _getMainSize(child);
assert(childSize <= maxChildExtent);
allocatedSize += childSize;
allocatedFlexSpace += maxChildExtent;
crossSize = math.max(crossSize, _getCrossSize(child));
}
}
}
// Align items along the main axis.
final idealSize = canFlex && mainAxisSize == MainAxisSize.max
? maxMainSize
: allocatedSize;
double? actualSize;
double actualSizeDelta;
late PdfPoint size;
switch (direction) {
case Axis.horizontal:
size = constraints.constrain(PdfPoint(idealSize, crossSize));
actualSize = size.x;
crossSize = size.y;
break;
case Axis.vertical:
size = constraints.constrain(PdfPoint(crossSize, idealSize));
actualSize = size.y;
crossSize = size.x;
break;
}
box = PdfRect.fromPoints(PdfPoint.zero, size);
actualSizeDelta = actualSize - allocatedSize;
final remainingSpace = math.max(0.0, actualSizeDelta);
double? leadingSpace;
late double betweenSpace;
final textDirection = Directionality.of(context);
final flipMainAxis =
!(_startIsTopLeft(direction, textDirection, verticalDirection) ?? true);
switch (mainAxisAlignment) {
case MainAxisAlignment.start:
leadingSpace = 0.0;
betweenSpace = 0.0;
break;
case MainAxisAlignment.end:
leadingSpace = remainingSpace;
betweenSpace = 0.0;
break;
case MainAxisAlignment.center:
leadingSpace = remainingSpace / 2.0;
betweenSpace = 0.0;
break;
case MainAxisAlignment.spaceBetween:
leadingSpace = 0.0;
betweenSpace = totalChildren > 1
? remainingSpace / (totalChildren - 1)
: 0.0;
break;
case MainAxisAlignment.spaceAround:
betweenSpace = totalChildren > 0 ? remainingSpace / totalChildren : 0.0;
leadingSpace = betweenSpace / 2.0;
break;
case MainAxisAlignment.spaceEvenly:
betweenSpace = totalChildren > 0
? remainingSpace / (totalChildren + 1)
: 0.0;
leadingSpace = betweenSpace;
break;
}
// Position elements
var childMainPosition = flipMainAxis
? actualSize - leadingSpace
: leadingSpace;
for (var child in children.sublist(
_context.firstChild,
_context.lastChild,
)) {
double? childCrossPosition;
switch (crossAxisAlignment) {
case CrossAxisAlignment.start:
case CrossAxisAlignment.end:
childCrossPosition =
_startIsTopLeft(
flipAxis(direction),
textDirection,
verticalDirection,
) ==
(crossAxisAlignment == CrossAxisAlignment.start)
? 0.0
: crossSize - _getCrossSize(child);
break;
case CrossAxisAlignment.center:
childCrossPosition = crossSize / 2.0 - _getCrossSize(child) / 2.0;
break;
case CrossAxisAlignment.stretch:
childCrossPosition = 0.0;
break;
}
if (flipMainAxis) {
childMainPosition -= _getMainSize(child);
}
switch (direction) {
case Axis.horizontal:
child.box = PdfRect(
box!.left + childMainPosition,
box!.bottom + childCrossPosition,
child.box!.width,
child.box!.height,
);
break;
case Axis.vertical:
child.box = PdfRect(
childCrossPosition,
childMainPosition,
child.box!.width,
child.box!.height,
);
break;
}
if (flipMainAxis) {
childMainPosition -= betweenSpace;
} else {
childMainPosition += _getMainSize(child) + betweenSpace;
}
}
}