mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Add DataColumn.headingRowAlignment for DataTable (#144006)
fixes [[`DataTable`] Unable to center the label of a DataColumn without side effects](https://github.com/flutter/flutter/issues/143340) ### Code sample <details> <summary>expand to view the code sample</summary> ```dart import 'package:flutter/material.dart'; void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, home: MaterialApp( home: Material( child: DataTable( columns: <DataColumn>[ DataColumn( headingRowAlignment: MainAxisAlignment.center, onSort: (int columnIndex, bool ascending) {}, label: const Text('Header'), ), ], sortColumnIndex: 0, rows: const <DataRow>[ DataRow( cells: <DataCell>[ DataCell(Center(child: Text('Data'))), ], ), ], ), ), ), ); } } ``` </details> ### Center `DataColumn.mainAxisAlignment` without sort arrow  ### Center `DataColumn.mainAxisAlignment` with sort arrow 
This commit is contained in:
parent
984c69a86f
commit
40dda2b4bb
@ -42,6 +42,7 @@ class DataColumn {
|
||||
this.numeric = false,
|
||||
this.onSort,
|
||||
this.mouseCursor,
|
||||
this.headingRowAlignment,
|
||||
});
|
||||
|
||||
/// The column heading.
|
||||
@ -97,6 +98,17 @@ class DataColumn {
|
||||
/// See also:
|
||||
/// * [MaterialStateMouseCursor], which can be used to create a [MouseCursor].
|
||||
final MaterialStateProperty<MouseCursor?>? mouseCursor;
|
||||
|
||||
/// Defines the horizontal layout of the [label] and sort indicator in the
|
||||
/// heading row.
|
||||
///
|
||||
/// If [headingRowAlignment] value is [MainAxisAlignment.center] and [onSort] is
|
||||
/// not null, then a [SizedBox] with a width of sort arrow icon size and sort
|
||||
/// arrow padding will be placed before the [label] to ensure the label is
|
||||
/// centered in the column.
|
||||
///
|
||||
/// If null, then defaults to [MainAxisAlignment.start].
|
||||
final MainAxisAlignment? headingRowAlignment;
|
||||
}
|
||||
|
||||
/// Row configuration and cell data for a [DataTable].
|
||||
@ -830,12 +842,16 @@ class DataTable extends StatelessWidget {
|
||||
required bool ascending,
|
||||
required MaterialStateProperty<Color?>? overlayColor,
|
||||
required MouseCursor? mouseCursor,
|
||||
required MainAxisAlignment headingRowAlignment,
|
||||
}) {
|
||||
final ThemeData themeData = Theme.of(context);
|
||||
final DataTableThemeData dataTableTheme = DataTableTheme.of(context);
|
||||
label = Row(
|
||||
textDirection: numeric ? TextDirection.rtl : null,
|
||||
mainAxisAlignment: headingRowAlignment,
|
||||
children: <Widget>[
|
||||
if (headingRowAlignment == MainAxisAlignment.center && onSort != null)
|
||||
const SizedBox(width: _SortArrowState._arrowIconSize + _sortArrowPadding),
|
||||
label,
|
||||
if (onSort != null)
|
||||
...<Widget>[
|
||||
@ -1117,6 +1133,7 @@ class DataTable extends StatelessWidget {
|
||||
ascending: sortAscending,
|
||||
overlayColor: effectiveHeadingRowColor,
|
||||
mouseCursor: column.mouseCursor?.resolve(headerStates) ?? dataTableTheme.headingCellCursor?.resolve(headerStates),
|
||||
headingRowAlignment: column.headingRowAlignment ?? dataTableTheme.headingRowAlignment ?? MainAxisAlignment.start,
|
||||
);
|
||||
rowIndex = 1;
|
||||
for (final DataRow row in rows) {
|
||||
|
||||
@ -57,6 +57,7 @@ class DataTableThemeData with Diagnosticable {
|
||||
this.checkboxHorizontalMargin,
|
||||
this.headingCellCursor,
|
||||
this.dataRowCursor,
|
||||
this.headingRowAlignment,
|
||||
}) : assert(dataRowMinHeight == null || dataRowMaxHeight == null || dataRowMaxHeight >= dataRowMinHeight),
|
||||
assert(dataRowHeight == null || (dataRowMinHeight == null && dataRowMaxHeight == null),
|
||||
'dataRowHeight ($dataRowHeight) must not be set if dataRowMinHeight ($dataRowMinHeight) or dataRowMaxHeight ($dataRowMaxHeight) are set.'),
|
||||
@ -114,6 +115,9 @@ class DataTableThemeData with Diagnosticable {
|
||||
/// If specified, overrides the default value of [DataRow.mouseCursor].
|
||||
final MaterialStateProperty<MouseCursor?>? dataRowCursor;
|
||||
|
||||
/// If specified, overrides the default value of [DataColumn.headingRowAlignment].
|
||||
final MainAxisAlignment? headingRowAlignment;
|
||||
|
||||
/// Creates a copy of this object but with the given fields replaced with the
|
||||
/// new values.
|
||||
DataTableThemeData copyWith({
|
||||
@ -136,6 +140,7 @@ class DataTableThemeData with Diagnosticable {
|
||||
double? checkboxHorizontalMargin,
|
||||
MaterialStateProperty<MouseCursor?>? headingCellCursor,
|
||||
MaterialStateProperty<MouseCursor?>? dataRowCursor,
|
||||
MainAxisAlignment? headingRowAlignment,
|
||||
}) {
|
||||
assert(dataRowHeight == null || (dataRowMinHeight == null && dataRowMaxHeight == null),
|
||||
'dataRowHeight ($dataRowHeight) must not be set if dataRowMinHeight ($dataRowMinHeight) or dataRowMaxHeight ($dataRowMaxHeight) are set.');
|
||||
@ -157,6 +162,7 @@ class DataTableThemeData with Diagnosticable {
|
||||
checkboxHorizontalMargin: checkboxHorizontalMargin ?? this.checkboxHorizontalMargin,
|
||||
headingCellCursor: headingCellCursor ?? this.headingCellCursor,
|
||||
dataRowCursor: dataRowCursor ?? this.dataRowCursor,
|
||||
headingRowAlignment: headingRowAlignment ?? this.headingRowAlignment,
|
||||
);
|
||||
}
|
||||
|
||||
@ -182,6 +188,7 @@ class DataTableThemeData with Diagnosticable {
|
||||
checkboxHorizontalMargin: lerpDouble(a.checkboxHorizontalMargin, b.checkboxHorizontalMargin, t),
|
||||
headingCellCursor: t < 0.5 ? a.headingCellCursor : b.headingCellCursor,
|
||||
dataRowCursor: t < 0.5 ? a.dataRowCursor : b.dataRowCursor,
|
||||
headingRowAlignment: t < 0.5 ? a.headingRowAlignment : b.headingRowAlignment,
|
||||
);
|
||||
}
|
||||
|
||||
@ -201,6 +208,7 @@ class DataTableThemeData with Diagnosticable {
|
||||
checkboxHorizontalMargin,
|
||||
headingCellCursor,
|
||||
dataRowCursor,
|
||||
headingRowAlignment,
|
||||
);
|
||||
|
||||
@override
|
||||
@ -225,7 +233,8 @@ class DataTableThemeData with Diagnosticable {
|
||||
&& other.dividerThickness == dividerThickness
|
||||
&& other.checkboxHorizontalMargin == checkboxHorizontalMargin
|
||||
&& other.headingCellCursor == headingCellCursor
|
||||
&& other.dataRowCursor == dataRowCursor;
|
||||
&& other.dataRowCursor == dataRowCursor
|
||||
&& other.headingRowAlignment == headingRowAlignment;
|
||||
}
|
||||
|
||||
@override
|
||||
@ -245,6 +254,7 @@ class DataTableThemeData with Diagnosticable {
|
||||
properties.add(DoubleProperty('checkboxHorizontalMargin', checkboxHorizontalMargin, defaultValue: null));
|
||||
properties.add(DiagnosticsProperty<MaterialStateProperty<MouseCursor?>?>('headingCellCursor', headingCellCursor, defaultValue: null));
|
||||
properties.add(DiagnosticsProperty<MaterialStateProperty<MouseCursor?>?>('dataRowCursor', dataRowCursor, defaultValue: null));
|
||||
properties.add(EnumProperty<MainAxisAlignment>('headingRowAlignment', headingRowAlignment, defaultValue: null));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2363,6 +2363,60 @@ void main() {
|
||||
final TextStyle? dataTextStyle = _getTextRenderObject(tester, 'Data 1').text.style;
|
||||
expect(dataTextStyle, defaultTextStyle.style);
|
||||
});
|
||||
|
||||
// This is a regression test for https://github.com/flutter/flutter/issues/143340.
|
||||
testWidgets('DataColumn label can be centered', (WidgetTester tester) async {
|
||||
const double horizontalMargin = 24.0;
|
||||
|
||||
Widget buildTable({ MainAxisAlignment? headingRowAlignment, bool sortEnabled = false }) {
|
||||
return MaterialApp(
|
||||
home: Material(
|
||||
child: DataTable(
|
||||
columns: <DataColumn>[
|
||||
DataColumn(
|
||||
headingRowAlignment: headingRowAlignment,
|
||||
onSort: sortEnabled
|
||||
? (int columnIndex, bool ascending) { }
|
||||
: null,
|
||||
label: const Text('Header'),
|
||||
),
|
||||
],
|
||||
rows: const <DataRow>[
|
||||
DataRow(
|
||||
cells: <DataCell>[
|
||||
DataCell(Text('Data')),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Test mainAxisAlignment without sort arrow.
|
||||
await tester.pumpWidget(buildTable());
|
||||
|
||||
Offset headerTopLeft = tester.getTopLeft(find.text('Header'));
|
||||
expect(headerTopLeft.dx, equals(horizontalMargin));
|
||||
|
||||
// Test mainAxisAlignment.center without sort arrow.
|
||||
await tester.pumpWidget(buildTable(headingRowAlignment: MainAxisAlignment.center));
|
||||
|
||||
Offset headerCenter = tester.getCenter(find.text('Header'));
|
||||
expect(headerCenter.dx, equals(400));
|
||||
|
||||
// Test mainAxisAlignment with sort arrow.
|
||||
await tester.pumpWidget(buildTable(sortEnabled: true));
|
||||
|
||||
headerTopLeft = tester.getTopLeft(find.text('Header'));
|
||||
expect(headerTopLeft.dx, equals(horizontalMargin));
|
||||
|
||||
// Test mainAxisAlignment.center with sort arrow.
|
||||
await tester.pumpWidget(buildTable(headingRowAlignment: MainAxisAlignment.center, sortEnabled: true));
|
||||
|
||||
headerCenter = tester.getCenter(find.text('Header'));
|
||||
expect(headerCenter.dx, equals(400));
|
||||
});
|
||||
}
|
||||
|
||||
RenderParagraph _getTextRenderObject(WidgetTester tester, String text) {
|
||||
|
||||
@ -45,6 +45,7 @@ void main() {
|
||||
expect(themeData.checkboxHorizontalMargin, null);
|
||||
expect(themeData.headingCellCursor, null);
|
||||
expect(themeData.dataRowCursor, null);
|
||||
expect(themeData.headingRowAlignment, null);
|
||||
|
||||
const DataTableTheme theme = DataTableTheme(data: DataTableThemeData(), child: SizedBox());
|
||||
expect(theme.data.decoration, null);
|
||||
@ -62,6 +63,7 @@ void main() {
|
||||
expect(theme.data.checkboxHorizontalMargin, null);
|
||||
expect(theme.data.headingCellCursor, null);
|
||||
expect(theme.data.dataRowCursor, null);
|
||||
expect(theme.data.headingRowAlignment, null);
|
||||
});
|
||||
|
||||
testWidgets('Default DataTableThemeData debugFillProperties', (WidgetTester tester) async {
|
||||
@ -97,6 +99,7 @@ void main() {
|
||||
checkboxHorizontalMargin: 6.0,
|
||||
headingCellCursor: const MaterialStatePropertyAll<MouseCursor>(SystemMouseCursors.grab),
|
||||
dataRowCursor: const MaterialStatePropertyAll<MouseCursor>(SystemMouseCursors.forbidden),
|
||||
headingRowAlignment: MainAxisAlignment.center,
|
||||
).debugFillProperties(builder);
|
||||
|
||||
final List<String> description = builder.properties
|
||||
@ -118,6 +121,7 @@ void main() {
|
||||
expect(description[11], 'checkboxHorizontalMargin: 6.0');
|
||||
expect(description[12], 'headingCellCursor: WidgetStatePropertyAll(SystemMouseCursor(grab))');
|
||||
expect(description[13], 'dataRowCursor: WidgetStatePropertyAll(SystemMouseCursor(forbidden))');
|
||||
expect(description[14], 'headingRowAlignment: center');
|
||||
});
|
||||
|
||||
testWidgets('DataTable is themeable', (WidgetTester tester) async {
|
||||
@ -546,6 +550,67 @@ void main() {
|
||||
|
||||
expect(tester.getSize(_findFirstContainerFor('Data')).height, localThemeDataRowHeight);
|
||||
});
|
||||
|
||||
// This is a regression test for https://github.com/flutter/flutter/issues/143340.
|
||||
testWidgets('DataColumn label can be centered with DataTableTheme.headingRowAlignment', (WidgetTester tester) async {
|
||||
const double horizontalMargin = 24.0;
|
||||
|
||||
Widget buildTable({ MainAxisAlignment? headingRowAlignment, bool sortEnabled = false }) {
|
||||
return MaterialApp(
|
||||
theme: ThemeData(
|
||||
dataTableTheme: DataTableThemeData(
|
||||
headingRowAlignment: headingRowAlignment,
|
||||
),
|
||||
),
|
||||
home: Material(
|
||||
child: DataTable(
|
||||
columns: <DataColumn>[
|
||||
DataColumn(
|
||||
onSort: sortEnabled
|
||||
? (int columnIndex, bool ascending) { }
|
||||
: null,
|
||||
label: const Text('Header'),
|
||||
),
|
||||
],
|
||||
rows: const <DataRow>[
|
||||
DataRow(
|
||||
cells: <DataCell>[
|
||||
DataCell(Text('Data')),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Test mainAxisAlignment without sort arrow.
|
||||
await tester.pumpWidget(buildTable());
|
||||
|
||||
Offset headerTopLeft = tester.getTopLeft(find.text('Header'));
|
||||
expect(headerTopLeft.dx, equals(horizontalMargin));
|
||||
|
||||
// Test mainAxisAlignment.center without sort arrow.
|
||||
await tester.pumpWidget(buildTable(headingRowAlignment: MainAxisAlignment.center));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
Offset headerCenter = tester.getCenter(find.text('Header'));
|
||||
expect(headerCenter.dx, equals(400));
|
||||
|
||||
// Test mainAxisAlignment with sort arrow.
|
||||
await tester.pumpWidget(buildTable(sortEnabled: true));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
headerTopLeft = tester.getTopLeft(find.text('Header'));
|
||||
expect(headerTopLeft.dx, equals(horizontalMargin));
|
||||
|
||||
// Test mainAxisAlignment.center with sort arrow.
|
||||
await tester.pumpWidget(buildTable(headingRowAlignment: MainAxisAlignment.center, sortEnabled: true));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
headerCenter = tester.getCenter(find.text('Header'));
|
||||
expect(headerCenter.dx, equals(400));
|
||||
});
|
||||
}
|
||||
|
||||
BoxDecoration _tableRowBoxDecoration({required WidgetTester tester, required int index}) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user