From 793569f621f79119dd7cb2c82c403b5d52271946 Mon Sep 17 00:00:00 2001 From: Lam Thanh Nhan Date: Sat, 23 Nov 2024 00:34:00 +0700 Subject: [PATCH] Add `columnWidth` Property to `DataTable` for Customizable Column Widths (#159279) After discussing with @Hixie in #51799, this PR has been created to implement the ability to customize column widths in DataTable. This change introduces the `columnWidth` property to `DataColumn`, allowing developers to specify column widths using `TableColumnWidth` within a `DataTable`. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [ ] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md --- .../flutter/lib/src/material/data_table.dart | 22 ++++- .../test/material/data_table_test.dart | 86 +++++++++++++++++++ 2 files changed, 107 insertions(+), 1 deletion(-) diff --git a/packages/flutter/lib/src/material/data_table.dart b/packages/flutter/lib/src/material/data_table.dart index 06c5430ee0..a8d773b2dd 100644 --- a/packages/flutter/lib/src/material/data_table.dart +++ b/packages/flutter/lib/src/material/data_table.dart @@ -42,6 +42,7 @@ class DataColumn { /// Creates the configuration for a column of a [DataTable]. const DataColumn({ required this.label, + this.columnWidth, this.tooltip, this.numeric = false, this.onSort, @@ -67,6 +68,22 @@ class DataColumn { /// The label should not include the sort indicator. final Widget label; + /// How the horizontal extents of this column of the table should be determined. + /// + /// The [FixedColumnWidth] class can be used to specify a specific width in + /// pixels. This is the cheapest way to size a table's columns. + /// + /// The layout performance of the table depends critically on which column + /// sizing algorithms are used here. In particular, [IntrinsicColumnWidth] is + /// quite expensive because it needs to measure each cell in the column to + /// determine the intrinsic size of the column. + /// + /// If this property is `null`, the table applies a default behavior: + /// - If the table has exactly one column identified as the only text column + /// (i.e., all the rest are numeric), that column uses `IntrinsicColumnWidth(flex: 1.0)`. + /// - All other columns use `IntrinsicColumnWidth()`. + final TableColumnWidth? columnWidth; + /// The column heading's tooltip. /// /// This is a longer description of the column heading, for cases @@ -1122,11 +1139,14 @@ class DataTable extends StatelessWidget { start: paddingStart, end: paddingEnd, ); - if (dataColumnIndex == _onlyTextColumn) { + if (column.columnWidth != null) { + tableColumns[displayColumnIndex] = column.columnWidth!; + } else if (dataColumnIndex == _onlyTextColumn) { tableColumns[displayColumnIndex] = const IntrinsicColumnWidth(flex: 1.0); } else { tableColumns[displayColumnIndex] = const IntrinsicColumnWidth(); } + final Set headerStates = { if (column.onSort == null) MaterialState.disabled, diff --git a/packages/flutter/test/material/data_table_test.dart b/packages/flutter/test/material/data_table_test.dart index cabf72c656..a1035fd7b0 100644 --- a/packages/flutter/test/material/data_table_test.dart +++ b/packages/flutter/test/material/data_table_test.dart @@ -2417,6 +2417,92 @@ void main() { headerCenter = tester.getCenter(find.text('Header')); expect(headerCenter.dx, equals(400)); }); + + testWidgets('DataTable with custom column widths - checkbox', (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + home: Material( + child: SizedBox( + width: 500, + child: DataTable( + columns: const [ + DataColumn( + label: Text('Flex Numeric'), + columnWidth: FlexColumnWidth(), + numeric: true, + ), + DataColumn( + label: Text('Numeric'), + numeric: true, + ), + DataColumn( + label: Text('Text'), + ), + ], + rows: [ + DataRow( + onSelectChanged: (bool? value) {}, + cells: const [ + DataCell(Text('1')), + DataCell(Text('1')), + DataCell(Text('D')), + ], + ), + ], + ), + ), + ), + ) + ); + + final Table table = tester.widget(find.byType(Table)); + expect(table.columnWidths![0], isA()); // Checkbox column + expect(table.columnWidths![1], const FlexColumnWidth()); + expect(table.columnWidths![2], const IntrinsicColumnWidth()); + expect(table.columnWidths![3], const IntrinsicColumnWidth(flex: 1)); + }); + + testWidgets('DataTable with custom column widths - no checkbox', (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + home: Material( + child: SizedBox( + width: 500, + child: DataTable( + columns: const [ + DataColumn( + label: Text('Flex Numeric'), + columnWidth: FlexColumnWidth(), + numeric: true, + ), + DataColumn( + label: Text('Numeric'), + numeric: true, + ), + DataColumn( + label: Text('Text'), + ), + ], + rows: const [ + DataRow( + cells: [ + DataCell(Text('1')), + DataCell(Text('1')), + DataCell(Text('D')), + ], + ), + ], + ), + ), + ), + ) + ); + + final Table table = tester.widget(find.byType(Table)); + expect(table.columnWidths![0], const FlexColumnWidth()); + expect(table.columnWidths![1], const IntrinsicColumnWidth()); + expect(table.columnWidths![2], const IntrinsicColumnWidth(flex: 1)); + }); } RenderParagraph _getTextRenderObject(WidgetTester tester, String text) {