-
Notifications
You must be signed in to change notification settings - Fork 29.7k
Description
Certain combinations of width constraints for a table and the number of flex columns results in a logic error that collapses the flex columns to their minimum widths. If their minimum width is zero, then they are collapsed to zero width and are not rendered.
The following code reproduces the problem:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final blocks = <Widget>[
Container(height: 60, color: Colors.red,),
Container(height: 60, color: Colors.green,),
Container(height: 60, color: Colors.blue,),
Container(height: 60, color: Colors.yellow,),
Container(height: 60, color: Colors.brown,),
Container(height: 60, color: Colors.purple,),
];
return Scaffold(
body: Container(
width: 100.0,
child: Table(
children: [TableRow(children: blocks)],
),
),
);
}
}This code should produce a 100 unit wide rectangle consisting of 6 colored blocks, but it renders an empty screen. If you comment out one of the Containers in the blocks list, it will correctly render a 100 unit wide rectangle with 5 colored blocks.
I tracked down the problem and it is a logic error in RenderTable._computeColumnWidths(BoxConstraints constraints)
The logic error is in the following loop at the end of _computeColumnWidths()
if (deficit > 0.0) {
// Now we have to take out the remaining space from the
// columns that aren't minimum sized.
// To make this fair, we repeatedly remove equal amounts from
// each column, clamped to the minimum width, until we run out
// of columns that aren't at their minWidth.
do {
final double delta = deficit / availableColumns;
int newAvailableColumns = 0;
for (int x = 0; x < columns; x += 1) {
final double availableDelta = widths[x] - minWidths[x];
if (availableDelta > 0.0) {
if (availableDelta <= delta) {
// shrank to minimum
deficit -= widths[x] - minWidths[x];
widths[x] = minWidths[x];
} else {
deficit -= availableDelta;
widths[x] -= availableDelta;
newAvailableColumns += 1;
}
}
}
availableColumns = newAvailableColumns;
} while (deficit > 0.0 && availableColumns > 0);
}After the flex columns are grown to fill the width constraint and if the table size is now greater than the constraint, this loop is supposed to evenly remove a portion of the deficit from each flex column to bring the table width into the constraint. Instead it removes all of flex space in each column and in the example code reduces every column to zero width.
The error is in the last if/else statement:
if (availableDelta <= delta) {
// shrank to minimum
deficit -= widths[x] - minWidths[x];
widths[x] = minWidths[x];
} else {
deficit -= availableDelta;
widths[x] -= availableDelta;
newAvailableColumns += 1;
}The delta variable was calculated to be the even portion of the deficit that is to be subtracted from each column:
final double delta = deficit / availableColumns;
The if/else statement is supposed to remove the availableDelta from a column width if availableDelta is less than delta, otherwise it should be subtracting delta from the columnWidth.
Instead, if availableDelta is greater than delta, it's removing all of availableDelta from the column width, which collapses it to its minimum width.
This code
} else {
deficit -= availableDelta;
widths[x] -= availableDelta;
newAvailableColumns += 1;
}should instead be
} else {
deficit -= delta;
widths[x] -= delta;
newAvailableColumns += 1;
}