computeTableSize method

TableLayoutResult computeTableSize(
  1. BoxConstraints constraints, [
  2. IntrinsicComputer? intrinsicComputer
])

Implementation

TableLayoutResult computeTableSize(BoxConstraints constraints,
    [IntrinsicComputer? intrinsicComputer]) {
  double flexWidth = 0;
  double flexHeight = 0;
  double fixedWidth = 0;
  double fixedHeight = 0;

  Map<int, double> columnWidths = {};
  Map<int, double> rowHeights = {};

  int maxRow = 0;
  int maxColumn = 0;

  bool hasTightFlexWidth = false;
  bool hasTightFlexHeight = false;

  // find the maximum row and column
  RenderBox? child = firstChild;
  while (child != null) {
    final parentData = child.parentData as TableParentData;
    if (parentData.computeSize) {
      int? column = parentData.column;
      int? row = parentData.row;
      if (column != null && row != null) {
        int columnSpan = parentData.columnSpan ?? 1;
        int rowSpan = parentData.rowSpan ?? 1;
        maxColumn = max(maxColumn, column + columnSpan - 1);
        maxRow = max(maxRow, row + rowSpan - 1);
      }
    }
    child = childAfter(child);
  }

  // micro-optimization: avoid calculating flexes if there are no flexes
  bool hasFlexWidth = false;
  bool hasFlexHeight = false;

  // row
  for (int r = 0; r <= maxRow; r++) {
    final heightConstraint = _height(r);
    if (heightConstraint is FlexTableSize &&
        constraints.hasBoundedHeight &&
        intrinsicComputer == null) {
      flexHeight += heightConstraint.flex;
      hasFlexHeight = true;
      if (heightConstraint.fit == FlexFit.tight) {
        hasTightFlexHeight = true;
      }
    } else if (heightConstraint is FixedTableSize) {
      fixedHeight += heightConstraint.value;
      rowHeights[r] = max(rowHeights[r] ?? 0, heightConstraint.value);
    }
  }
  // column
  for (int c = 0; c <= maxColumn; c++) {
    final widthConstraint = _width(c);
    if (widthConstraint is FlexTableSize && constraints.hasBoundedWidth) {
      flexWidth += widthConstraint.flex;
      hasFlexWidth = true;
      if (widthConstraint.fit == FlexFit.tight) {
        hasTightFlexWidth = true;
      }
    } else if (widthConstraint is FixedTableSize) {
      fixedWidth += widthConstraint.value;
      columnWidths[c] = max(columnWidths[c] ?? 0, widthConstraint.value);
    }
  }

  double spacePerFlexWidth = 0;
  double spacePerFlexHeight = 0;
  double remainingWidth;
  double remainingHeight;
  if (constraints.hasBoundedWidth) {
    remainingWidth = constraints.maxWidth - fixedWidth;
  } else {
    remainingWidth = double.infinity;
  }
  if (constraints.hasBoundedHeight) {
    remainingHeight = constraints.maxHeight - fixedHeight;
  } else {
    remainingHeight = double.infinity;
  }

  // find the proper intrinsic sizes (if any)
  child = lastChild;
  while (child != null) {
    final parentData = child.parentData as TableParentData;
    if (parentData.computeSize) {
      int? column = parentData.column;
      int? row = parentData.row;
      if (column != null && row != null) {
        final widthConstraint = _width(column);
        final heightConstraint = _height(row);
        if (widthConstraint is IntrinsicTableSize ||
            (widthConstraint is FlexTableSize && intrinsicComputer != null)) {
          var extent = rowHeights[row] ?? remainingHeight;
          double maxIntrinsicWidth = intrinsicComputer != null
              ? intrinsicComputer(child, extent)
              : child.getMaxIntrinsicWidth(extent);
          maxIntrinsicWidth = min(maxIntrinsicWidth, remainingWidth);
          int columnSpan = parentData.columnSpan ?? 1;
          // distribute the intrinsic width to all columns
          maxIntrinsicWidth = maxIntrinsicWidth / columnSpan;
          for (int i = 0; i < columnSpan; i++) {
            columnWidths[column + i] =
                max(columnWidths[column + i] ?? 0, maxIntrinsicWidth);
          }
        }
        if (heightConstraint is IntrinsicTableSize ||
            (heightConstraint is FlexTableSize &&
                intrinsicComputer != null)) {
          var extent = columnWidths[column] ?? remainingWidth;
          double maxIntrinsicHeight = intrinsicComputer != null
              ? intrinsicComputer(child, extent)
              : child.getMaxIntrinsicHeight(extent);
          maxIntrinsicHeight = min(maxIntrinsicHeight, remainingHeight);
          int rowSpan = parentData.rowSpan ?? 1;
          // distribute the intrinsic height to all rows
          maxIntrinsicHeight = maxIntrinsicHeight / rowSpan;
          for (int i = 0; i < rowSpan; i++) {
            rowHeights[row + i] =
                max(columnWidths[row + i] ?? 0, maxIntrinsicHeight);
          }
        }
      }
    }
    child = childBefore(child);
  }

  double usedColumnWidth = columnWidths.values.fold(0, (a, b) => a + b);
  double usedRowHeight = rowHeights.values.fold(0, (a, b) => a + b);
  double looseRemainingWidth = remainingWidth;
  double looseRemainingHeight = remainingHeight;
  double looseSpacePerFlexWidth = 0;
  double looseSpacePerFlexHeight = 0;

  if (intrinsicComputer == null) {
    // recalculate remaining space for flexes
    if (constraints.hasBoundedWidth) {
      remainingWidth = constraints.maxWidth - usedColumnWidth;
    } else {
      remainingWidth = double.infinity;
    }
    if (constraints.hasInfiniteWidth) {
      looseRemainingWidth = double.infinity;
    } else {
      looseRemainingWidth = max(0, constraints.minWidth - usedColumnWidth);
    }
    if (constraints.hasBoundedHeight) {
      remainingHeight = constraints.maxHeight - usedRowHeight;
    } else {
      remainingHeight = double.infinity;
    }
    if (constraints.hasInfiniteHeight) {
      looseRemainingHeight = double.infinity;
    } else {
      looseRemainingHeight = max(0, constraints.minHeight - usedRowHeight);
    }
    if (flexWidth > 0 && remainingWidth > 0) {
      spacePerFlexWidth = remainingWidth / flexWidth;
    } else {
      spacePerFlexWidth = 0;
    }
    if (flexWidth > 0 && looseRemainingWidth > 0) {
      looseSpacePerFlexWidth = looseRemainingWidth / flexWidth;
    }
    if (flexHeight > 0 && remainingHeight > 0) {
      spacePerFlexHeight = remainingHeight / flexHeight;
    } else {
      spacePerFlexHeight = 0;
    }
    if (flexHeight > 0 && looseRemainingHeight > 0) {
      spacePerFlexHeight = looseRemainingHeight / flexHeight;
    }

    // calculate space used for flexes
    if (hasFlexWidth) {
      for (int c = 0; c <= maxColumn; c++) {
        final widthConstraint = _width(c);
        if (widthConstraint is FlexTableSize) {
          // columnWidths[c] = widthConstraint.flex * spacePerFlexWidth;
          if (widthConstraint.fit == FlexFit.tight || hasTightFlexWidth) {
            columnWidths[c] = widthConstraint.flex * spacePerFlexWidth;
          } else {
            columnWidths[c] = widthConstraint.flex * looseSpacePerFlexWidth;
          }
        }
      }
    }
    if (hasFlexHeight) {
      for (int r = 0; r <= maxRow; r++) {
        final heightConstraint = _height(r);
        if (heightConstraint is FlexTableSize) {
          // rowHeights[r] = heightConstraint.flex * spacePerFlexHeight;
          if (heightConstraint.fit == FlexFit.tight || hasTightFlexHeight) {
            rowHeights[r] = heightConstraint.flex * spacePerFlexHeight;
          } else {
            rowHeights[r] = heightConstraint.flex * looseSpacePerFlexHeight;
          }
        }
      }
    }
  }

  // convert the column widths and row heights to a list, where missing values are 0
  List<double> columnWidthsList = List.generate(maxColumn + 1, (index) {
    return columnWidths[index] ?? 0;
  });
  columnWidths.forEach((key, value) {
    columnWidthsList[key] = value;
  });
  List<double> rowHeightsList =
      // List.filled(rowHeights.keys.reduce(max) + 1, 0);
      List.generate(maxRow + 1, (index) {
    return rowHeights[index] ?? 0;
  });
  rowHeights.forEach((key, value) {
    rowHeightsList[key] = value;
  });
  return TableLayoutResult(
    columnWidths: columnWidthsList,
    rowHeights: rowHeightsList,
    remainingWidth: remainingWidth,
    remainingHeight: remainingHeight,
    remainingLooseWidth: looseRemainingWidth,
    remainingLooseHeight: looseRemainingHeight,
    hasTightFlexWidth: hasTightFlexWidth,
    hasTightFlexHeight: hasTightFlexHeight,
  );
}