Skip to content

Commit ef5a6da

Browse files
authored
Fix a DataTable crash and improve some docs (flutter#100959)
1 parent fd360c4 commit ef5a6da

File tree

4 files changed

+68
-18
lines changed

4 files changed

+68
-18
lines changed

examples/api/lib/material/data_table/data_table.0.dart

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,21 +33,27 @@ class MyStatelessWidget extends StatelessWidget {
3333
return DataTable(
3434
columns: const <DataColumn>[
3535
DataColumn(
36-
label: Text(
37-
'Name',
38-
style: TextStyle(fontStyle: FontStyle.italic),
36+
label: Expanded(
37+
child: Text(
38+
'Name',
39+
style: TextStyle(fontStyle: FontStyle.italic),
40+
),
3941
),
4042
),
4143
DataColumn(
42-
label: Text(
43-
'Age',
44-
style: TextStyle(fontStyle: FontStyle.italic),
44+
label: Expanded(
45+
child: Text(
46+
'Age',
47+
style: TextStyle(fontStyle: FontStyle.italic),
48+
),
4549
),
4650
),
4751
DataColumn(
48-
label: Text(
49-
'Role',
50-
style: TextStyle(fontStyle: FontStyle.italic),
52+
label: Expanded(
53+
child: Text(
54+
'Role',
55+
style: TextStyle(fontStyle: FontStyle.italic),
56+
),
5157
),
5258
),
5359
],

packages/flutter/lib/src/material/data_table.dart

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,14 @@ class DataColumn {
4747
/// [Icon] (typically using size 18), or a [Row] with an icon and
4848
/// some text.
4949
///
50-
/// By default, this widget will only occupy the minimal space. If you want
51-
/// it to take the entire remaining space, e.g. when you want to use [Center],
52-
/// you can wrap it with an [Expanded].
50+
/// The [label] is placed within a [Row] along with the
51+
/// sort indicator (if applicable). By default, [label] only occupy minimal
52+
/// space. It is recommended to place the label content in an [Expanded] or
53+
/// [Flexible] as [label] to control how the content flexes. Otherwise,
54+
/// an exception will occur when the available space is insufficient.
55+
///
56+
/// By default, [DefaultTextStyle.softWrap] of this subtree will be set to false.
57+
/// Use [DefaultTextStyle.merge] to override it if needed.
5358
///
5459
/// The label should not include the sort indicator.
5560
final Widget label;

packages/flutter/lib/src/rendering/table.dart

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -988,6 +988,7 @@ class RenderTable extends RenderBox {
988988
// cache the table geometry for painting purposes
989989
final List<double> _rowTops = <double>[];
990990
Iterable<double>? _columnLefts;
991+
late double _tableWidth;
991992

992993
/// Returns the position and dimensions of the box that the given
993994
/// row covers, in this render object's coordinate space (so the
@@ -1050,26 +1051,26 @@ class RenderTable extends RenderBox {
10501051
if (rows * columns == 0) {
10511052
// TODO(ianh): if columns is zero, this should be zero width
10521053
// TODO(ianh): if columns is not zero, this should be based on the column width specifications
1054+
_tableWidth = 0.0;
10531055
size = constraints.constrain(Size.zero);
10541056
return;
10551057
}
10561058
final List<double> widths = _computeColumnWidths(constraints);
10571059
final List<double> positions = List<double>.filled(columns, 0.0);
1058-
final double tableWidth;
10591060
switch (textDirection) {
10601061
case TextDirection.rtl:
10611062
positions[columns - 1] = 0.0;
10621063
for (int x = columns - 2; x >= 0; x -= 1)
10631064
positions[x] = positions[x+1] + widths[x+1];
10641065
_columnLefts = positions.reversed;
1065-
tableWidth = positions.first + widths.first;
1066+
_tableWidth = positions.first + widths.first;
10661067
break;
10671068
case TextDirection.ltr:
10681069
positions[0] = 0.0;
10691070
for (int x = 1; x < columns; x += 1)
10701071
positions[x] = positions[x-1] + widths[x-1];
10711072
_columnLefts = positions;
1072-
tableWidth = positions.last + widths.last;
1073+
_tableWidth = positions.last + widths.last;
10731074
break;
10741075
}
10751076
_rowTops.clear();
@@ -1150,7 +1151,7 @@ class RenderTable extends RenderBox {
11501151
rowTop += rowHeight;
11511152
}
11521153
_rowTops.add(rowTop);
1153-
size = constraints.constrain(Size(tableWidth, rowTop));
1154+
size = constraints.constrain(Size(_tableWidth, rowTop));
11541155
assert(_rowTops.length == rows + 1);
11551156
}
11561157

@@ -1181,7 +1182,7 @@ class RenderTable extends RenderBox {
11811182
assert(_children.length == rows * columns);
11821183
if (rows * columns == 0) {
11831184
if (border != null) {
1184-
final Rect borderRect = Rect.fromLTWH(offset.dx, offset.dy, size.width, 0.0);
1185+
final Rect borderRect = Rect.fromLTWH(offset.dx, offset.dy, _tableWidth, 0.0);
11851186
border!.paint(context.canvas, borderRect, rows: const <double>[], columns: const <double>[]);
11861187
}
11871188
return;
@@ -1216,7 +1217,7 @@ class RenderTable extends RenderBox {
12161217
// The border rect might not fill the entire height of this render object
12171218
// if the rows underflow. We always force the columns to fill the width of
12181219
// the render object, which means the columns cannot underflow.
1219-
final Rect borderRect = Rect.fromLTWH(offset.dx, offset.dy, size.width, _rowTops.last);
1220+
final Rect borderRect = Rect.fromLTWH(offset.dx, offset.dy, _tableWidth, _rowTops.last);
12201221
final Iterable<double> rows = _rowTops.getRange(1, _rowTops.length - 1);
12211222
final Iterable<double> columns = _columnLefts!.skip(1);
12221223
border!.paint(context.canvas, borderRect, rows: rows, columns: columns);

packages/flutter/test/material/data_table_test.dart

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1855,4 +1855,42 @@ void main() {
18551855
expect(tableBorder?.bottom.width, null);
18561856
expect(tableBorder?.top.color, null);
18571857
});
1858+
1859+
// Regression test for https://github.com/flutter/flutter/issues/100952
1860+
testWidgets('Do not crashes when paint borders in a narrow space', (WidgetTester tester) async {
1861+
const List<DataColumn> columns = <DataColumn>[
1862+
DataColumn(label: Text('column1')),
1863+
DataColumn(label: Text('column2')),
1864+
];
1865+
1866+
const List<DataCell> cells = <DataCell>[
1867+
DataCell(Text('cell1')),
1868+
DataCell(Text('cell2')),
1869+
];
1870+
1871+
const List<DataRow> rows = <DataRow>[
1872+
DataRow(cells: cells),
1873+
DataRow(cells: cells),
1874+
];
1875+
1876+
await tester.pumpWidget(
1877+
MaterialApp(
1878+
home: Material(
1879+
child: Center(
1880+
child: SizedBox(
1881+
width: 117.0,
1882+
child: DataTable(
1883+
border: TableBorder.all(width: 2, color: Colors.red),
1884+
columns: columns,
1885+
rows: rows,
1886+
),
1887+
),
1888+
),
1889+
),
1890+
),
1891+
);
1892+
1893+
// Go without crashes.
1894+
1895+
});
18581896
}

0 commit comments

Comments
 (0)