效果展示
关键代码
@override
void performLayout() {
assert(_debugHasNecessaryDirections);
_hasVisualOverflow = false;
RenderBox child = firstChild;
if (child == null) {
size = constraints.smallest;
return;
}
BoxConstraints childConstraints;
double mainAxisLimit = 0.0;
bool flipMainAxis = false;
bool flipCrossAxis = false;
switch (direction) {
case Axis.horizontal:
childConstraints = BoxConstraints(maxWidth: constraints.maxWidth);
mainAxisLimit = constraints.maxWidth;
if (textDirection == TextDirection.rtl) flipMainAxis = true;
if (verticalDirection == VerticalDirection.up) flipCrossAxis = true;
break;
case Axis.vertical:
childConstraints = BoxConstraints(maxHeight: constraints.maxHeight);
mainAxisLimit = constraints.maxHeight;
if (verticalDirection == VerticalDirection.up) flipMainAxis = true;
if (textDirection == TextDirection.rtl) flipCrossAxis = true;
break;
}
assert(childConstraints != null);
assert(mainAxisLimit != null);
final double spacing = this.spacing;
final double runSpacing = this.runSpacing;
final List<_RunMetrics> runMetrics = <_RunMetrics>[];
double mainAxisExtent = 0.0;
double crossAxisExtent = 0.0;
double runMainAxisExtent = 0.0;
double runCrossAxisExtent = 0.0;
int childCount = 0;
//主轴宽度永远为mainAxisLimit
mainAxisExtent = mainAxisLimit;
while (child != null) {
child.layout(childConstraints, parentUsesSize: true);
double childMainAxisExtent = _getMainAxisExtent(child);
double childCrossAxisExtent = _getCrossAxisExtent(child);
if (childCount > 0 && runMainAxisExtent + spacing + childMainAxisExtent > mainAxisLimit) {
// 计算空余空间
final mainAxisFreeSpace = mainAxisLimit - (runMainAxisExtent + spacing);
// mainAxisExtent = math.max(mainAxisExtent, runMainAxisExtent);
crossAxisExtent += runCrossAxisExtent;
if (runMetrics.isNotEmpty) crossAxisExtent += runSpacing;
final needFillSpace = mainAxisFreeSpace > 0.0;
// debugPrint("index=$childCount,freeSize=$mainAxisFreeSpace,"
// "needFillLast=$needFillSpace,");
// 如果剩余空间为0,那么不必处理剩余空间
if (!needFillSpace) {
runMetrics.add(_RunMetrics(runMainAxisExtent, runCrossAxisExtent, childCount));
}
// 否则
else {
// 重新调整runCrossAxisExtent
runCrossAxisExtent = math.max(runCrossAxisExtent, childCrossAxisExtent);
// 重新调整childCount
childCount += 1;
// 每一行矩形右下角坐标(x,y,widget index)=>(mainAxisExtent,runCrossAxisExtent,childCount)
// x==mainAxisExtent意味着充满这一行
runMetrics.add(_RunMetrics(mainAxisExtent, runCrossAxisExtent, childCount));
// 修复最后一个Widget的尺寸
BoxConstraints spaceConstraints;
switch (direction) {
case Axis.horizontal:
spaceConstraints = BoxConstraints(maxWidth: mainAxisFreeSpace);
break;
case Axis.vertical:
spaceConstraints = BoxConstraints(maxHeight: mainAxisFreeSpace);
break;
}
child.layout(spaceConstraints, parentUsesSize: true);
// 跳过下一个child,我们会将它安排在本行末,并修正它所在的行数
final FilledWrapParentData childParentData = child.parentData;
childParentData._runIndex = runMetrics.length - 1;
child = childParentData.nextSibling;
if (child == null) break;
// 重新测量新的child
child.layout(childConstraints, parentUsesSize: true);
childMainAxisExtent = _getMainAxisExtent(child);
childCrossAxisExtent = _getCrossAxisExtent(child);
}
runMainAxisExtent = 0.0;
runCrossAxisExtent = 0.0;
childCount = 0;
}
runMainAxisExtent += childMainAxisExtent;
if (childCount > 0) runMainAxisExtent += spacing;
runCrossAxisExtent = math.max(runCrossAxisExtent, childCrossAxisExtent);
childCount += 1;
final FilledWrapParentData childParentData = child.parentData;
childParentData._runIndex = runMetrics.length;
child = childParentData.nextSibling;
···
}
下一个计划