measureColumns<T, K> static method
Measures every column once (the expensive, text-layout part) and
splits the result into fixed columns (selectable/expandable slots,
and any header with an explicit TTableHeader.minWidth/maxWidth —
those are honored exactly as given, no auto-sizing applied) versus
auto columns that still need their natural width turned into a final
pixel width once the available table width is known.
Callers should cache the returned TTableColumnMeasurements and only call TTableColumnMeasurements.resolve on layout/resize — that part is pure arithmetic and re-runs no text measurement.
Implementation
static TTableColumnMeasurements measureColumns<T, K>(
List<TTableHeader<T, K>> headers,
bool selectable,
bool expandable, {
int maxTreeLevel = 0,
bool isHierarchical = false,
List<T> sampleItems = const [],
TextStyle? headerTextStyle,
TextStyle? contentTextStyle,
}) {
final fixedWidths = <int, TableColumnWidth>{};
int columnIndex = 0;
double fixedTotal = 0;
if (selectable) {
fixedWidths[columnIndex] = const FixedColumnWidth(50);
fixedTotal += 50;
columnIndex++;
}
if (expandable) {
fixedWidths[columnIndex] = const FixedColumnWidth(50);
fixedTotal += 50;
columnIndex++;
}
final effectiveTreeLevel = isHierarchical && maxTreeLevel == 0 ? 1 : maxTreeLevel;
final treeIndentBonus = (isHierarchical || maxTreeLevel > 0) ? (effectiveTreeLevel * 16.0 + 36.0) : 0.0;
final autoIndices = <int>[];
final autoBasisWidths = <double>[]; // weighting basis, already clamped into [min, max]
final autoMinWidths = <double?>[];
final autoMaxWidths = <double?>[];
for (int i = 0; i < headers.length; i++) {
final header = headers[i];
final extraWidth = (i == 0) ? treeIndentBonus : 0.0;
final index = columnIndex + i;
final hasMin = header.minWidth != null && header.minWidth! > 0;
final hasMax = header.maxWidth != null && header.maxWidth! != double.infinity;
// Only skip measurement entirely when min and max pin the SAME value —
// that's a genuinely fixed column with no range to size within, so
// there's no point paying for a TextPainter layout.
if (hasMin && hasMax && header.minWidth == header.maxWidth) {
final width = header.minWidth! + extraWidth;
fixedWidths[index] = FixedColumnWidth(width);
fixedTotal += width;
continue;
}
final natural = _naturalColumnWidth<T, K>(
header,
sampleItems,
headerTextStyle ?? fallbackTextStyle,
contentTextStyle ?? fallbackTextStyle,
) +
extraWidth;
final min = hasMin ? header.minWidth! + extraWidth : null;
final max = hasMax ? header.maxWidth! + extraWidth : null;
// Weighting basis = natural width clamped into this column's own
// [min, max] — so a maxWidth cap doesn't inflate what other columns
// compete for, and a minWidth-only column still measures/grows like
// any other auto column instead of freezing at exactly minWidth.
var basis = natural;
if (min != null && basis < min) basis = min;
if (max != null && basis > max) basis = max;
autoIndices.add(index);
autoBasisWidths.add(basis);
autoMinWidths.add(min);
autoMaxWidths.add(max);
}
return TTableColumnMeasurements(
fixedWidths: fixedWidths,
fixedTotal: fixedTotal,
autoIndices: autoIndices,
autoNaturalWidths: autoBasisWidths,
autoMinWidths: autoMinWidths,
autoMaxWidths: autoMaxWidths,
);
}