- override
Do the work of computing the layout for this render object.
Do not call this function directly: call layout
instead. This function
is called by layout
when there is actually work to be done by this
render object during layout. The layout constraints provided by your
parent are available via the constraints
getter.
If sizedByParent
is true, then this function should not actually change
the dimensions of this render object. Instead, that work should be done by
performResize
. If sizedByParent
is false, then this function should
both change the dimensions of this render object and instruct its children
to layout.
In implementing this function, you must call layout
on each of your
children, passing true for parentUsesSize if your layout information is
dependent on your child's layout information. Passing true for
parentUsesSize ensures that this render object will undergo layout if the
child undergoes layout. Otherwise, the child can changes its layout
information without informing this render object.
Source
@override void performLayout() { // Determine used flex factor, size inflexible items, calculate free space. int totalFlex = 0; int totalChildren = 0; assert(constraints != null); final double maxMainSize = _direction == Axis.horizontal ? constraints.maxWidth : constraints.maxHeight; final bool canFlex = maxMainSize < double.INFINITY; double crossSize = 0.0; double allocatedSize = 0.0; // Sum of the sizes of the the non-flexible children. RenderBox child = firstChild; while (child != null) { final FlexParentData childParentData = child.parentData; totalChildren++; final int flex = _getFlex(child); if (flex > 0) { assert(() { final String identity = _direction == Axis.horizontal ? 'row' : 'column'; final String axis = _direction == Axis.horizontal ? 'horizontal' : 'vertical'; final String dimension = _direction == Axis.horizontal ? 'width' : 'height'; String error, message; String addendum = ''; if (maxMainSize == double.INFINITY) { error = 'RenderFlex children have non-zero flex but incoming $dimension constraints are unbounded.'; message = 'When a $identity is in a parent that does not provide a finite $dimension constraint, for example ' 'if it is in a $axis scrollable, it will try to shrink-wrap its children along the $axis ' 'axis. Setting a flex on a child (e.g. using a Flexible) indicates that the child is to ' 'expand to fill the remaining space in the $axis direction.'; StringBuffer information = new StringBuffer(); RenderBox node = this; switch (_direction) { case Axis.horizontal: while (!node.constraints.hasBoundedWidth && node.parent is RenderBox) node = node.parent; if (!node.constraints.hasBoundedWidth) node = null; break; case Axis.vertical: while (!node.constraints.hasBoundedHeight && node.parent is RenderBox) node = node.parent; if (!node.constraints.hasBoundedHeight) node = null; break; } if (node != null) { information.writeln('The nearest ancestor providing an unbounded width constraint is:'); information.writeln(' $node'); List<String> description = <String>[]; node.debugFillDescription(description); for (String line in description) information.writeln(' $line'); } information.writeln('See also: https://flutter.io/layout/'); addendum = information.toString(); } else { return true; } throw new FlutterError( '$error\n' '$message\n' 'These two directives are mutually exclusive. If a parent is to shrink-wrap its child, the child ' 'cannot simultaneously expand to fit its parent.\n' 'The affected RenderFlex is:\n' ' $this\n' 'The creator information is set to:\n' ' $debugCreator\n' '$addendum' 'If this message did not help you determine the problem, consider using debugDumpRenderTree():\n' ' https://flutter.io/debugging/#rendering-layer\n' ' http://docs.flutter.io/flutter/rendering/debugDumpRenderTree.html\n' 'If none of the above helps enough to fix this problem, please don\'t hesitate to file a bug:\n' ' https://github.com/flutter/flutter/issues/new' ); }); totalFlex += childParentData.flex; } else { BoxConstraints innerConstraints; if (crossAxisAlignment == CrossAxisAlignment.stretch) { switch (_direction) { case Axis.horizontal: innerConstraints = new BoxConstraints(minHeight: constraints.maxHeight, maxHeight: constraints.maxHeight); break; case Axis.vertical: innerConstraints = new BoxConstraints(minWidth: constraints.maxWidth, maxWidth: constraints.maxWidth); break; } } else { switch (_direction) { case Axis.horizontal: innerConstraints = new BoxConstraints(maxHeight: constraints.maxHeight); break; case Axis.vertical: innerConstraints = new BoxConstraints(maxWidth: constraints.maxWidth); break; } } child.layout(innerConstraints, parentUsesSize: true); allocatedSize += _getMainSize(child); crossSize = math.max(crossSize, _getCrossSize(child)); } assert(child.parentData == childParentData); child = childParentData.nextSibling; } _overflow = math.max(0.0, allocatedSize - (canFlex ? maxMainSize : 0.0)); // Distribute free space to flexible children, and determine baseline. final double freeSpace = math.max(0.0, (canFlex ? maxMainSize : 0.0) - allocatedSize); double maxBaselineDistance = 0.0; if (totalFlex > 0 || crossAxisAlignment == CrossAxisAlignment.baseline) { final double spacePerFlex = totalFlex > 0 ? (freeSpace / totalFlex) : 0.0; child = firstChild; while (child != null) { final int flex = _getFlex(child); if (flex > 0) { final double maxChildExtent = spacePerFlex * flex; double minChildExtent; switch (_getFit(child)) { case FlexFit.tight: minChildExtent = maxChildExtent; break; case FlexFit.loose: minChildExtent = 0.0; break; } assert(minChildExtent != null); BoxConstraints innerConstraints; if (crossAxisAlignment == CrossAxisAlignment.stretch) { switch (_direction) { case Axis.horizontal: innerConstraints = new BoxConstraints(minWidth: minChildExtent, maxWidth: maxChildExtent, minHeight: constraints.maxHeight, maxHeight: constraints.maxHeight); break; case Axis.vertical: innerConstraints = new BoxConstraints(minWidth: constraints.maxWidth, maxWidth: constraints.maxWidth, minHeight: minChildExtent, maxHeight: maxChildExtent); break; } } else { switch (_direction) { case Axis.horizontal: innerConstraints = new BoxConstraints(minWidth: minChildExtent, maxWidth: maxChildExtent, maxHeight: constraints.maxHeight); break; case Axis.vertical: innerConstraints = new BoxConstraints(maxWidth: constraints.maxWidth, minHeight: minChildExtent, maxHeight: maxChildExtent); break; } } child.layout(innerConstraints, parentUsesSize: true); allocatedSize += _getMainSize(child); crossSize = math.max(crossSize, _getCrossSize(child)); } if (crossAxisAlignment == CrossAxisAlignment.baseline) { assert(() { if (textBaseline == null) throw new FlutterError('To use FlexAlignItems.baseline, you must also specify which baseline to use using the "baseline" argument.'); return true; }); double distance = child.getDistanceToBaseline(textBaseline, onlyReal: true); if (distance != null) maxBaselineDistance = math.max(maxBaselineDistance, distance); } final FlexParentData childParentData = child.parentData; child = childParentData.nextSibling; } } // Align items along the main axis. double leadingSpace; double betweenSpace; double remainingSpace; if (canFlex) { final bool isMainAxisSizeMax = mainAxisSize == MainAxisSize.max; final double preferredSize = isMainAxisSizeMax ? maxMainSize : allocatedSize; switch (_direction) { case Axis.horizontal: size = constraints.constrain(new Size(preferredSize, crossSize)); remainingSpace = math.max(0.0, size.width - allocatedSize); crossSize = size.height; assert(isMainAxisSizeMax ? size.width == maxMainSize : size.width >= constraints.minWidth); break; case Axis.vertical: size = constraints.constrain(new Size(crossSize, preferredSize)); remainingSpace = math.max(0.0, size.height - allocatedSize); crossSize = size.width; assert(isMainAxisSizeMax ? size.height == maxMainSize : size.height >= constraints.minHeight); break; } } else { leadingSpace = 0.0; betweenSpace = 0.0; switch (_direction) { case Axis.horizontal: size = constraints.constrain(new Size(_overflow, crossSize)); crossSize = size.height; remainingSpace = math.max(0.0, size.width - _overflow); break; case Axis.vertical: size = constraints.constrain(new Size(crossSize, _overflow)); crossSize = size.width; remainingSpace = math.max(0.0, size.height - _overflow); break; } _overflow = 0.0; } 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 double childMainPosition = leadingSpace; child = firstChild; while (child != null) { final FlexParentData childParentData = child.parentData; double childCrossPosition; switch (_crossAxisAlignment) { case CrossAxisAlignment.stretch: case CrossAxisAlignment.start: childCrossPosition = 0.0; break; case CrossAxisAlignment.end: childCrossPosition = crossSize - _getCrossSize(child); break; case CrossAxisAlignment.center: childCrossPosition = crossSize / 2.0 - _getCrossSize(child) / 2.0; break; case CrossAxisAlignment.baseline: childCrossPosition = 0.0; if (_direction == Axis.horizontal) { assert(textBaseline != null); double distance = child.getDistanceToBaseline(textBaseline, onlyReal: true); if (distance != null) childCrossPosition = maxBaselineDistance - distance; } break; } switch (_direction) { case Axis.horizontal: childParentData.offset = new Offset(childMainPosition, childCrossPosition); break; case Axis.vertical: childParentData.offset = new Offset(childCrossPosition, childMainPosition); break; } childMainPosition += _getMainSize(child) + betweenSpace; child = childParentData.nextSibling; } }