performLayout method Null safety
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 change its layout information without informing this render object.
Implementation
@override
void performLayout() {
final BoxConstraints constraints = this.constraints;
final int rows = this.rows;
final int columns = this.columns;
assert(_children.length == rows * columns);
if (rows * columns == 0) {
// TODO(ianh): if columns is zero, this should be zero width
// TODO(ianh): if columns is not zero, this should be based on the column width specifications
_tableWidth = 0.0;
size = constraints.constrain(Size.zero);
return;
}
final List<double> widths = _computeColumnWidths(constraints);
final List<double> positions = List<double>.filled(columns, 0.0);
switch (textDirection) {
case TextDirection.rtl:
positions[columns - 1] = 0.0;
for (int x = columns - 2; x >= 0; x -= 1) {
positions[x] = positions[x+1] + widths[x+1];
}
_columnLefts = positions.reversed;
_tableWidth = positions.first + widths.first;
break;
case TextDirection.ltr:
positions[0] = 0.0;
for (int x = 1; x < columns; x += 1) {
positions[x] = positions[x-1] + widths[x-1];
}
_columnLefts = positions;
_tableWidth = positions.last + widths.last;
break;
}
_rowTops.clear();
_baselineDistance = null;
// then, lay out each row
double rowTop = 0.0;
for (int y = 0; y < rows; y += 1) {
_rowTops.add(rowTop);
double rowHeight = 0.0;
bool haveBaseline = false;
double beforeBaselineDistance = 0.0;
double afterBaselineDistance = 0.0;
final List<double> baselines = List<double>.filled(columns, 0.0);
for (int x = 0; x < columns; x += 1) {
final int xy = x + y * columns;
final RenderBox? child = _children[xy];
if (child != null) {
final TableCellParentData childParentData = child.parentData! as TableCellParentData;
assert(childParentData != null);
childParentData.x = x;
childParentData.y = y;
switch (childParentData.verticalAlignment ?? defaultVerticalAlignment) {
case TableCellVerticalAlignment.baseline:
assert(textBaseline != null, 'An explicit textBaseline is required when using baseline alignment.');
child.layout(BoxConstraints.tightFor(width: widths[x]), parentUsesSize: true);
final double? childBaseline = child.getDistanceToBaseline(textBaseline!, onlyReal: true);
if (childBaseline != null) {
beforeBaselineDistance = math.max(beforeBaselineDistance, childBaseline);
afterBaselineDistance = math.max(afterBaselineDistance, child.size.height - childBaseline);
baselines[x] = childBaseline;
haveBaseline = true;
} else {
rowHeight = math.max(rowHeight, child.size.height);
childParentData.offset = Offset(positions[x], rowTop);
}
break;
case TableCellVerticalAlignment.top:
case TableCellVerticalAlignment.middle:
case TableCellVerticalAlignment.bottom:
child.layout(BoxConstraints.tightFor(width: widths[x]), parentUsesSize: true);
rowHeight = math.max(rowHeight, child.size.height);
break;
case TableCellVerticalAlignment.fill:
break;
}
}
}
if (haveBaseline) {
if (y == 0) {
_baselineDistance = beforeBaselineDistance;
}
rowHeight = math.max(rowHeight, beforeBaselineDistance + afterBaselineDistance);
}
for (int x = 0; x < columns; x += 1) {
final int xy = x + y * columns;
final RenderBox? child = _children[xy];
if (child != null) {
final TableCellParentData childParentData = child.parentData! as TableCellParentData;
switch (childParentData.verticalAlignment ?? defaultVerticalAlignment) {
case TableCellVerticalAlignment.baseline:
childParentData.offset = Offset(positions[x], rowTop + beforeBaselineDistance - baselines[x]);
break;
case TableCellVerticalAlignment.top:
childParentData.offset = Offset(positions[x], rowTop);
break;
case TableCellVerticalAlignment.middle:
childParentData.offset = Offset(positions[x], rowTop + (rowHeight - child.size.height) / 2.0);
break;
case TableCellVerticalAlignment.bottom:
childParentData.offset = Offset(positions[x], rowTop + rowHeight - child.size.height);
break;
case TableCellVerticalAlignment.fill:
child.layout(BoxConstraints.tightFor(width: widths[x], height: rowHeight));
childParentData.offset = Offset(positions[x], rowTop);
break;
}
}
}
rowTop += rowHeight;
}
_rowTops.add(rowTop);
size = constraints.constrain(Size(_tableWidth, rowTop));
assert(_rowTops.length == rows + 1);
}