Unverified Commit ef5a6da3 authored by xubaolin's avatar xubaolin Committed by GitHub

Fix a `DataTable` crash and improve some docs (#100959)

parent fd360c4a
...@@ -33,23 +33,29 @@ class MyStatelessWidget extends StatelessWidget { ...@@ -33,23 +33,29 @@ class MyStatelessWidget extends StatelessWidget {
return DataTable( return DataTable(
columns: const <DataColumn>[ columns: const <DataColumn>[
DataColumn( DataColumn(
label: Text( label: Expanded(
child: Text(
'Name', 'Name',
style: TextStyle(fontStyle: FontStyle.italic), style: TextStyle(fontStyle: FontStyle.italic),
), ),
), ),
),
DataColumn( DataColumn(
label: Text( label: Expanded(
child: Text(
'Age', 'Age',
style: TextStyle(fontStyle: FontStyle.italic), style: TextStyle(fontStyle: FontStyle.italic),
), ),
), ),
),
DataColumn( DataColumn(
label: Text( label: Expanded(
child: Text(
'Role', 'Role',
style: TextStyle(fontStyle: FontStyle.italic), style: TextStyle(fontStyle: FontStyle.italic),
), ),
), ),
),
], ],
rows: const <DataRow>[ rows: const <DataRow>[
DataRow( DataRow(
......
...@@ -47,9 +47,14 @@ class DataColumn { ...@@ -47,9 +47,14 @@ class DataColumn {
/// [Icon] (typically using size 18), or a [Row] with an icon and /// [Icon] (typically using size 18), or a [Row] with an icon and
/// some text. /// some text.
/// ///
/// By default, this widget will only occupy the minimal space. If you want /// The [label] is placed within a [Row] along with the
/// it to take the entire remaining space, e.g. when you want to use [Center], /// sort indicator (if applicable). By default, [label] only occupy minimal
/// you can wrap it with an [Expanded]. /// space. It is recommended to place the label content in an [Expanded] or
/// [Flexible] as [label] to control how the content flexes. Otherwise,
/// an exception will occur when the available space is insufficient.
///
/// By default, [DefaultTextStyle.softWrap] of this subtree will be set to false.
/// Use [DefaultTextStyle.merge] to override it if needed.
/// ///
/// The label should not include the sort indicator. /// The label should not include the sort indicator.
final Widget label; final Widget label;
......
...@@ -988,6 +988,7 @@ class RenderTable extends RenderBox { ...@@ -988,6 +988,7 @@ class RenderTable extends RenderBox {
// cache the table geometry for painting purposes // cache the table geometry for painting purposes
final List<double> _rowTops = <double>[]; final List<double> _rowTops = <double>[];
Iterable<double>? _columnLefts; Iterable<double>? _columnLefts;
late double _tableWidth;
/// Returns the position and dimensions of the box that the given /// Returns the position and dimensions of the box that the given
/// row covers, in this render object's coordinate space (so the /// row covers, in this render object's coordinate space (so the
...@@ -1050,26 +1051,26 @@ class RenderTable extends RenderBox { ...@@ -1050,26 +1051,26 @@ class RenderTable extends RenderBox {
if (rows * columns == 0) { if (rows * columns == 0) {
// TODO(ianh): if columns is zero, this should be zero width // 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 // TODO(ianh): if columns is not zero, this should be based on the column width specifications
_tableWidth = 0.0;
size = constraints.constrain(Size.zero); size = constraints.constrain(Size.zero);
return; return;
} }
final List<double> widths = _computeColumnWidths(constraints); final List<double> widths = _computeColumnWidths(constraints);
final List<double> positions = List<double>.filled(columns, 0.0); final List<double> positions = List<double>.filled(columns, 0.0);
final double tableWidth;
switch (textDirection) { switch (textDirection) {
case TextDirection.rtl: case TextDirection.rtl:
positions[columns - 1] = 0.0; positions[columns - 1] = 0.0;
for (int x = columns - 2; x >= 0; x -= 1) for (int x = columns - 2; x >= 0; x -= 1)
positions[x] = positions[x+1] + widths[x+1]; positions[x] = positions[x+1] + widths[x+1];
_columnLefts = positions.reversed; _columnLefts = positions.reversed;
tableWidth = positions.first + widths.first; _tableWidth = positions.first + widths.first;
break; break;
case TextDirection.ltr: case TextDirection.ltr:
positions[0] = 0.0; positions[0] = 0.0;
for (int x = 1; x < columns; x += 1) for (int x = 1; x < columns; x += 1)
positions[x] = positions[x-1] + widths[x-1]; positions[x] = positions[x-1] + widths[x-1];
_columnLefts = positions; _columnLefts = positions;
tableWidth = positions.last + widths.last; _tableWidth = positions.last + widths.last;
break; break;
} }
_rowTops.clear(); _rowTops.clear();
...@@ -1150,7 +1151,7 @@ class RenderTable extends RenderBox { ...@@ -1150,7 +1151,7 @@ class RenderTable extends RenderBox {
rowTop += rowHeight; rowTop += rowHeight;
} }
_rowTops.add(rowTop); _rowTops.add(rowTop);
size = constraints.constrain(Size(tableWidth, rowTop)); size = constraints.constrain(Size(_tableWidth, rowTop));
assert(_rowTops.length == rows + 1); assert(_rowTops.length == rows + 1);
} }
...@@ -1181,7 +1182,7 @@ class RenderTable extends RenderBox { ...@@ -1181,7 +1182,7 @@ class RenderTable extends RenderBox {
assert(_children.length == rows * columns); assert(_children.length == rows * columns);
if (rows * columns == 0) { if (rows * columns == 0) {
if (border != null) { if (border != null) {
final Rect borderRect = Rect.fromLTWH(offset.dx, offset.dy, size.width, 0.0); final Rect borderRect = Rect.fromLTWH(offset.dx, offset.dy, _tableWidth, 0.0);
border!.paint(context.canvas, borderRect, rows: const <double>[], columns: const <double>[]); border!.paint(context.canvas, borderRect, rows: const <double>[], columns: const <double>[]);
} }
return; return;
...@@ -1216,7 +1217,7 @@ class RenderTable extends RenderBox { ...@@ -1216,7 +1217,7 @@ class RenderTable extends RenderBox {
// The border rect might not fill the entire height of this render object // The border rect might not fill the entire height of this render object
// if the rows underflow. We always force the columns to fill the width of // if the rows underflow. We always force the columns to fill the width of
// the render object, which means the columns cannot underflow. // the render object, which means the columns cannot underflow.
final Rect borderRect = Rect.fromLTWH(offset.dx, offset.dy, size.width, _rowTops.last); final Rect borderRect = Rect.fromLTWH(offset.dx, offset.dy, _tableWidth, _rowTops.last);
final Iterable<double> rows = _rowTops.getRange(1, _rowTops.length - 1); final Iterable<double> rows = _rowTops.getRange(1, _rowTops.length - 1);
final Iterable<double> columns = _columnLefts!.skip(1); final Iterable<double> columns = _columnLefts!.skip(1);
border!.paint(context.canvas, borderRect, rows: rows, columns: columns); border!.paint(context.canvas, borderRect, rows: rows, columns: columns);
......
...@@ -1855,4 +1855,42 @@ void main() { ...@@ -1855,4 +1855,42 @@ void main() {
expect(tableBorder?.bottom.width, null); expect(tableBorder?.bottom.width, null);
expect(tableBorder?.top.color, null); expect(tableBorder?.top.color, null);
}); });
// Regression test for https://github.com/flutter/flutter/issues/100952
testWidgets('Do not crashes when paint borders in a narrow space', (WidgetTester tester) async {
const List<DataColumn> columns = <DataColumn>[
DataColumn(label: Text('column1')),
DataColumn(label: Text('column2')),
];
const List<DataCell> cells = <DataCell>[
DataCell(Text('cell1')),
DataCell(Text('cell2')),
];
const List<DataRow> rows = <DataRow>[
DataRow(cells: cells),
DataRow(cells: cells),
];
await tester.pumpWidget(
MaterialApp(
home: Material(
child: Center(
child: SizedBox(
width: 117.0,
child: DataTable(
border: TableBorder.all(width: 2, color: Colors.red),
columns: columns,
rows: rows,
),
),
),
),
),
);
// Go without crashes.
});
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment