mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Bumps the Dart version to 3.8 across the repo (excluding engine/src/flutter/third_party) and applies formatting updates from Dart 3.8. ## 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]. - [ ] I listed at least one issue that this PR fixes in the description above. - [ ] 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. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [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
710 lines
28 KiB
Dart
710 lines
28 KiB
Dart
// Copyright 2014 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
import 'package:flutter/gestures.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/rendering.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
void main() {
|
|
test('DataTableThemeData copyWith, ==, hashCode basics', () {
|
|
expect(const DataTableThemeData(), const DataTableThemeData().copyWith());
|
|
expect(const DataTableThemeData().hashCode, const DataTableThemeData().copyWith().hashCode);
|
|
});
|
|
|
|
test('DataTableThemeData copyWith dataRowHeight', () {
|
|
const DataTableThemeData themeData = DataTableThemeData(
|
|
dataRowMinHeight: 10,
|
|
dataRowMaxHeight: 10,
|
|
);
|
|
expect(themeData, themeData.copyWith());
|
|
expect(
|
|
themeData.copyWith(dataRowMinHeight: 20, dataRowMaxHeight: 20),
|
|
themeData.copyWith(dataRowHeight: 20),
|
|
);
|
|
});
|
|
|
|
test('DataTableThemeData lerp special cases', () {
|
|
const DataTableThemeData data = DataTableThemeData();
|
|
expect(identical(DataTableThemeData.lerp(data, data, 0.5), data), true);
|
|
});
|
|
|
|
test('DataTableThemeData defaults', () {
|
|
const DataTableThemeData themeData = DataTableThemeData();
|
|
expect(themeData.decoration, null);
|
|
expect(themeData.dataRowColor, null);
|
|
expect(themeData.dataRowHeight, null);
|
|
expect(themeData.dataRowMinHeight, null);
|
|
expect(themeData.dataRowMaxHeight, null);
|
|
expect(themeData.dataTextStyle, null);
|
|
expect(themeData.headingRowColor, null);
|
|
expect(themeData.headingRowHeight, null);
|
|
expect(themeData.headingTextStyle, null);
|
|
expect(themeData.horizontalMargin, null);
|
|
expect(themeData.columnSpacing, null);
|
|
expect(themeData.dividerThickness, null);
|
|
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);
|
|
expect(theme.data.dataRowColor, null);
|
|
expect(theme.data.dataRowHeight, null);
|
|
expect(theme.data.dataRowMinHeight, null);
|
|
expect(theme.data.dataRowMaxHeight, null);
|
|
expect(theme.data.dataTextStyle, null);
|
|
expect(theme.data.headingRowColor, null);
|
|
expect(theme.data.headingRowHeight, null);
|
|
expect(theme.data.headingTextStyle, null);
|
|
expect(theme.data.horizontalMargin, null);
|
|
expect(theme.data.columnSpacing, null);
|
|
expect(theme.data.dividerThickness, null);
|
|
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 {
|
|
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
|
|
const DataTableThemeData().debugFillProperties(builder);
|
|
|
|
final List<String> description = builder.properties
|
|
.where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
|
|
.map((DiagnosticsNode node) => node.toString())
|
|
.toList();
|
|
|
|
expect(description, <String>[]);
|
|
});
|
|
|
|
testWidgets('DataTableThemeData implements debugFillProperties', (WidgetTester tester) async {
|
|
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
|
|
DataTableThemeData(
|
|
decoration: const BoxDecoration(color: Color(0xfffffff0)),
|
|
dataRowColor: MaterialStateProperty.resolveWith<Color>(
|
|
(Set<MaterialState> states) => const Color(0xfffffff1),
|
|
),
|
|
dataRowMinHeight: 41.0,
|
|
dataRowMaxHeight: 42.0,
|
|
dataTextStyle: const TextStyle(fontSize: 12.0),
|
|
headingRowColor: MaterialStateProperty.resolveWith<Color>(
|
|
(Set<MaterialState> states) => const Color(0xfffffff2),
|
|
),
|
|
headingRowHeight: 52.0,
|
|
headingTextStyle: const TextStyle(fontSize: 14.0),
|
|
horizontalMargin: 3.0,
|
|
columnSpacing: 4.0,
|
|
dividerThickness: 5.0,
|
|
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
|
|
.where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
|
|
.map((DiagnosticsNode node) => node.toString())
|
|
.toList();
|
|
|
|
expect(description[0], 'decoration: BoxDecoration(color: ${const Color(0xfffffff0)})');
|
|
expect(description[1], "dataRowColor: Instance of '_WidgetStatePropertyWith<Color>'");
|
|
expect(description[2], 'dataRowMinHeight: 41.0');
|
|
expect(description[3], 'dataRowMaxHeight: 42.0');
|
|
expect(description[4], 'dataTextStyle: TextStyle(inherit: true, size: 12.0)');
|
|
expect(description[5], "headingRowColor: Instance of '_WidgetStatePropertyWith<Color>'");
|
|
expect(description[6], 'headingRowHeight: 52.0');
|
|
expect(description[7], 'headingTextStyle: TextStyle(inherit: true, size: 14.0)');
|
|
expect(description[8], 'horizontalMargin: 3.0');
|
|
expect(description[9], 'columnSpacing: 4.0');
|
|
expect(description[10], 'dividerThickness: 5.0');
|
|
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 {
|
|
const BoxDecoration decoration = BoxDecoration(color: Color(0xfffffff0));
|
|
const MaterialStateProperty<Color> dataRowColor = MaterialStatePropertyAll<Color>(
|
|
Color(0xfffffff1),
|
|
);
|
|
const double minMaxDataRowHeight = 41.0;
|
|
const TextStyle dataTextStyle = TextStyle(fontSize: 12.5);
|
|
const MaterialStateProperty<Color> headingRowColor = MaterialStatePropertyAll<Color>(
|
|
Color(0xfffffff2),
|
|
);
|
|
const double headingRowHeight = 52.0;
|
|
const TextStyle headingTextStyle = TextStyle(fontSize: 14.5);
|
|
const double horizontalMargin = 3.0;
|
|
const double columnSpacing = 4.0;
|
|
const double dividerThickness = 5.0;
|
|
const MaterialStateProperty<MouseCursor> headingCellCursor =
|
|
MaterialStatePropertyAll<MouseCursor>(SystemMouseCursors.grab);
|
|
const MaterialStateProperty<MouseCursor> dataRowCursor = MaterialStatePropertyAll<MouseCursor>(
|
|
SystemMouseCursors.forbidden,
|
|
);
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(
|
|
dataTableTheme: const DataTableThemeData(
|
|
decoration: decoration,
|
|
dataRowColor: dataRowColor,
|
|
dataRowMinHeight: minMaxDataRowHeight,
|
|
dataRowMaxHeight: minMaxDataRowHeight,
|
|
dataTextStyle: dataTextStyle,
|
|
headingRowColor: headingRowColor,
|
|
headingRowHeight: headingRowHeight,
|
|
headingTextStyle: headingTextStyle,
|
|
horizontalMargin: horizontalMargin,
|
|
columnSpacing: columnSpacing,
|
|
dividerThickness: dividerThickness,
|
|
headingCellCursor: headingCellCursor,
|
|
dataRowCursor: dataRowCursor,
|
|
),
|
|
),
|
|
home: Scaffold(
|
|
body: DataTable(
|
|
sortColumnIndex: 0,
|
|
showCheckboxColumn: false,
|
|
columns: <DataColumn>[
|
|
DataColumn(label: const Text('A'), onSort: (int columnIndex, bool ascending) {}),
|
|
const DataColumn(label: Text('B')),
|
|
],
|
|
rows: <DataRow>[
|
|
DataRow(
|
|
cells: const <DataCell>[DataCell(Text('Data')), DataCell(Text('Data 2'))],
|
|
onSelectChanged: (bool? value) {},
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
final Finder tableContainerFinder = find.ancestor(
|
|
of: find.byType(Table),
|
|
matching: find.byType(Container),
|
|
);
|
|
expect(tester.widgetList<Container>(tableContainerFinder).first.decoration, decoration);
|
|
|
|
final TextStyle dataRowTextStyle = tester
|
|
.renderObject<RenderParagraph>(find.text('Data'))
|
|
.text
|
|
.style!;
|
|
expect(dataRowTextStyle.fontSize, dataTextStyle.fontSize);
|
|
expect(
|
|
_tableRowBoxDecoration(tester: tester, index: 1).color,
|
|
dataRowColor.resolve(<MaterialState>{}),
|
|
);
|
|
expect(_tableRowBoxDecoration(tester: tester, index: 1).border!.top.width, dividerThickness);
|
|
expect(tester.getSize(_findFirstContainerFor('Data')).height, minMaxDataRowHeight);
|
|
|
|
final TextStyle headingRowTextStyle = tester
|
|
.renderObject<RenderParagraph>(find.text('A'))
|
|
.text
|
|
.style!;
|
|
expect(headingRowTextStyle.fontSize, headingTextStyle.fontSize);
|
|
expect(
|
|
_tableRowBoxDecoration(tester: tester, index: 0).color,
|
|
headingRowColor.resolve(<MaterialState>{}),
|
|
);
|
|
|
|
expect(tester.getSize(_findFirstContainerFor('A')).height, headingRowHeight);
|
|
expect(tester.getTopLeft(find.text('A')).dx, horizontalMargin);
|
|
expect(
|
|
tester.getTopLeft(find.text('Data 2')).dx - tester.getTopRight(find.text('Data')).dx,
|
|
columnSpacing,
|
|
);
|
|
|
|
final TestGesture gesture = await tester.createGesture(
|
|
kind: PointerDeviceKind.mouse,
|
|
pointer: 1,
|
|
);
|
|
await gesture.addPointer(location: tester.getCenter(find.text('A')));
|
|
await tester.pump();
|
|
|
|
expect(
|
|
RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1),
|
|
SystemMouseCursors.grab,
|
|
);
|
|
|
|
await gesture.moveTo(tester.getCenter(find.text('Data')));
|
|
await tester.pump();
|
|
|
|
expect(
|
|
RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1),
|
|
SystemMouseCursors.forbidden,
|
|
);
|
|
});
|
|
|
|
testWidgets('DataTable is themeable - separate test for deprecated dataRowHeight', (
|
|
WidgetTester tester,
|
|
) async {
|
|
const double dataRowHeight = 51.0;
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(dataTableTheme: const DataTableThemeData(dataRowHeight: dataRowHeight)),
|
|
home: Scaffold(
|
|
body: DataTable(
|
|
sortColumnIndex: 0,
|
|
columns: <DataColumn>[
|
|
DataColumn(label: const Text('A'), onSort: (int columnIndex, bool ascending) {}),
|
|
const DataColumn(label: Text('B')),
|
|
],
|
|
rows: const <DataRow>[
|
|
DataRow(cells: <DataCell>[DataCell(Text('Data')), DataCell(Text('Data 2'))]),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(tester.getSize(_findFirstContainerFor('Data')).height, dataRowHeight);
|
|
});
|
|
|
|
testWidgets('DataTable properties are taken over the theme values', (WidgetTester tester) async {
|
|
const BoxDecoration themeDecoration = BoxDecoration(color: Color(0xfffffff1));
|
|
const MaterialStateProperty<Color> themeDataRowColor = MaterialStatePropertyAll<Color>(
|
|
Color(0xfffffff0),
|
|
);
|
|
const double minMaxThemeDataRowHeight = 50.0;
|
|
const TextStyle themeDataTextStyle = TextStyle(fontSize: 11.5);
|
|
const MaterialStateProperty<Color> themeHeadingRowColor = MaterialStatePropertyAll<Color>(
|
|
Color(0xfffffff1),
|
|
);
|
|
const double themeHeadingRowHeight = 51.0;
|
|
const TextStyle themeHeadingTextStyle = TextStyle(fontSize: 13.5);
|
|
const double themeHorizontalMargin = 2.0;
|
|
const double themeColumnSpacing = 3.0;
|
|
const double themeDividerThickness = 4.0;
|
|
const MaterialStateProperty<MouseCursor> themeHeadingCellCursor =
|
|
MaterialStatePropertyAll<MouseCursor>(SystemMouseCursors.copy);
|
|
const MaterialStateProperty<MouseCursor> themeDataRowCursor =
|
|
MaterialStatePropertyAll<MouseCursor>(SystemMouseCursors.copy);
|
|
|
|
const BoxDecoration decoration = BoxDecoration(color: Color(0xfffffff0));
|
|
const MaterialStateProperty<Color> dataRowColor = MaterialStatePropertyAll<Color>(
|
|
Color(0xfffffff1),
|
|
);
|
|
const double minMaxDataRowHeight = 51.0;
|
|
const TextStyle dataTextStyle = TextStyle(fontSize: 12.5);
|
|
const MaterialStateProperty<Color> headingRowColor = MaterialStatePropertyAll<Color>(
|
|
Color(0xfffffff2),
|
|
);
|
|
const double headingRowHeight = 52.0;
|
|
const TextStyle headingTextStyle = TextStyle(fontSize: 14.5);
|
|
const double horizontalMargin = 3.0;
|
|
const double columnSpacing = 4.0;
|
|
const double dividerThickness = 5.0;
|
|
const MaterialStateProperty<MouseCursor> headingCellCursor =
|
|
MaterialStatePropertyAll<MouseCursor>(SystemMouseCursors.forbidden);
|
|
const MaterialStateProperty<MouseCursor> dataRowCursor = MaterialStatePropertyAll<MouseCursor>(
|
|
SystemMouseCursors.forbidden,
|
|
);
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(
|
|
dataTableTheme: const DataTableThemeData(
|
|
decoration: themeDecoration,
|
|
dataRowColor: themeDataRowColor,
|
|
dataRowMinHeight: minMaxThemeDataRowHeight,
|
|
dataRowMaxHeight: minMaxThemeDataRowHeight,
|
|
dataTextStyle: themeDataTextStyle,
|
|
headingRowColor: themeHeadingRowColor,
|
|
headingRowHeight: themeHeadingRowHeight,
|
|
headingTextStyle: themeHeadingTextStyle,
|
|
horizontalMargin: themeHorizontalMargin,
|
|
columnSpacing: themeColumnSpacing,
|
|
dividerThickness: themeDividerThickness,
|
|
headingCellCursor: themeHeadingCellCursor,
|
|
dataRowCursor: themeDataRowCursor,
|
|
),
|
|
),
|
|
home: Scaffold(
|
|
body: DataTable(
|
|
showCheckboxColumn: false,
|
|
decoration: decoration,
|
|
dataRowColor: dataRowColor,
|
|
dataRowMinHeight: minMaxDataRowHeight,
|
|
dataRowMaxHeight: minMaxDataRowHeight,
|
|
dataTextStyle: dataTextStyle,
|
|
headingRowColor: headingRowColor,
|
|
headingRowHeight: headingRowHeight,
|
|
headingTextStyle: headingTextStyle,
|
|
horizontalMargin: horizontalMargin,
|
|
columnSpacing: columnSpacing,
|
|
dividerThickness: dividerThickness,
|
|
sortColumnIndex: 0,
|
|
columns: <DataColumn>[
|
|
DataColumn(
|
|
label: const Text('A'),
|
|
mouseCursor: headingCellCursor,
|
|
onSort: (int columnIndex, bool ascending) {},
|
|
),
|
|
const DataColumn(label: Text('B')),
|
|
],
|
|
rows: <DataRow>[
|
|
DataRow(
|
|
mouseCursor: dataRowCursor,
|
|
onSelectChanged: (bool? selected) {},
|
|
cells: const <DataCell>[DataCell(Text('Data')), DataCell(Text('Data 2'))],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
final Finder tableContainerFinder = find.ancestor(
|
|
of: find.byType(Table),
|
|
matching: find.byType(Container),
|
|
);
|
|
expect(tester.widget<Container>(tableContainerFinder).decoration, decoration);
|
|
|
|
final TextStyle dataRowTextStyle = tester
|
|
.renderObject<RenderParagraph>(find.text('Data'))
|
|
.text
|
|
.style!;
|
|
expect(dataRowTextStyle.fontSize, dataTextStyle.fontSize);
|
|
expect(
|
|
_tableRowBoxDecoration(tester: tester, index: 1).color,
|
|
dataRowColor.resolve(<MaterialState>{}),
|
|
);
|
|
expect(_tableRowBoxDecoration(tester: tester, index: 1).border!.top.width, dividerThickness);
|
|
expect(tester.getSize(_findFirstContainerFor('Data')).height, minMaxDataRowHeight);
|
|
|
|
final TextStyle headingRowTextStyle = tester
|
|
.renderObject<RenderParagraph>(find.text('A'))
|
|
.text
|
|
.style!;
|
|
expect(headingRowTextStyle.fontSize, headingTextStyle.fontSize);
|
|
expect(
|
|
_tableRowBoxDecoration(tester: tester, index: 0).color,
|
|
headingRowColor.resolve(<MaterialState>{}),
|
|
);
|
|
|
|
expect(tester.getSize(_findFirstContainerFor('A')).height, headingRowHeight);
|
|
expect(tester.getTopLeft(find.text('A')).dx, horizontalMargin);
|
|
expect(
|
|
tester.getTopLeft(find.text('Data 2')).dx - tester.getTopRight(find.text('Data')).dx,
|
|
columnSpacing,
|
|
);
|
|
|
|
final TestGesture gesture = await tester.createGesture(
|
|
kind: PointerDeviceKind.mouse,
|
|
pointer: 1,
|
|
);
|
|
await gesture.addPointer(location: tester.getCenter(find.text('A')));
|
|
await tester.pump();
|
|
|
|
expect(
|
|
RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1),
|
|
headingCellCursor.resolve(<MaterialState>{}),
|
|
);
|
|
|
|
await gesture.moveTo(tester.getCenter(find.text('Data')));
|
|
await tester.pump();
|
|
|
|
expect(
|
|
RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1),
|
|
dataRowCursor.resolve(<MaterialState>{}),
|
|
);
|
|
});
|
|
|
|
testWidgets(
|
|
'DataTable properties are taken over the theme values - separate test for deprecated dataRowHeight',
|
|
(WidgetTester tester) async {
|
|
const double themeDataRowHeight = 50.0;
|
|
const double dataRowHeight = 51.0;
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(
|
|
dataTableTheme: const DataTableThemeData(dataRowHeight: themeDataRowHeight),
|
|
),
|
|
home: Scaffold(
|
|
body: DataTable(
|
|
dataRowHeight: dataRowHeight,
|
|
sortColumnIndex: 0,
|
|
columns: <DataColumn>[
|
|
DataColumn(label: const Text('A'), onSort: (int columnIndex, bool ascending) {}),
|
|
const DataColumn(label: Text('B')),
|
|
],
|
|
rows: const <DataRow>[
|
|
DataRow(cells: <DataCell>[DataCell(Text('Data')), DataCell(Text('Data 2'))]),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(tester.getSize(_findFirstContainerFor('Data')).height, dataRowHeight);
|
|
},
|
|
);
|
|
|
|
testWidgets('Local DataTableTheme can override global DataTableTheme', (
|
|
WidgetTester tester,
|
|
) async {
|
|
const BoxDecoration globalThemeDecoration = BoxDecoration(color: Color(0xfffffff1));
|
|
const MaterialStateProperty<Color> globalThemeDataRowColor = MaterialStatePropertyAll<Color>(
|
|
Color(0xfffffff0),
|
|
);
|
|
const double minMaxGlobalThemeDataRowHeight = 50.0;
|
|
const TextStyle globalThemeDataTextStyle = TextStyle(fontSize: 11.5);
|
|
const MaterialStateProperty<Color> globalThemeHeadingRowColor = MaterialStatePropertyAll<Color>(
|
|
Color(0xfffffff1),
|
|
);
|
|
const double globalThemeHeadingRowHeight = 51.0;
|
|
const TextStyle globalThemeHeadingTextStyle = TextStyle(fontSize: 13.5);
|
|
const double globalThemeHorizontalMargin = 2.0;
|
|
const double globalThemeColumnSpacing = 3.0;
|
|
const double globalThemeDividerThickness = 4.0;
|
|
const MaterialStateProperty<MouseCursor> globalHeadingCellCursor =
|
|
MaterialStatePropertyAll<MouseCursor>(SystemMouseCursors.allScroll);
|
|
const MaterialStateProperty<MouseCursor> globalDataRowCursor =
|
|
MaterialStatePropertyAll<MouseCursor>(SystemMouseCursors.allScroll);
|
|
|
|
const BoxDecoration localThemeDecoration = BoxDecoration(color: Color(0xfffffff0));
|
|
const MaterialStateProperty<Color> localThemeDataRowColor = MaterialStatePropertyAll<Color>(
|
|
Color(0xfffffff1),
|
|
);
|
|
const double minMaxLocalThemeDataRowHeight = 51.0;
|
|
const TextStyle localThemeDataTextStyle = TextStyle(fontSize: 12.5);
|
|
const MaterialStateProperty<Color> localThemeHeadingRowColor = MaterialStatePropertyAll<Color>(
|
|
Color(0xfffffff2),
|
|
);
|
|
const double localThemeHeadingRowHeight = 52.0;
|
|
const TextStyle localThemeHeadingTextStyle = TextStyle(fontSize: 14.5);
|
|
const double localThemeHorizontalMargin = 3.0;
|
|
const double localThemeColumnSpacing = 4.0;
|
|
const double localThemeDividerThickness = 5.0;
|
|
const MaterialStateProperty<MouseCursor> localHeadingCellCursor =
|
|
MaterialStatePropertyAll<MouseCursor>(SystemMouseCursors.move);
|
|
const MaterialStateProperty<MouseCursor> localDataRowCursor =
|
|
MaterialStatePropertyAll<MouseCursor>(SystemMouseCursors.move);
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(
|
|
dataTableTheme: const DataTableThemeData(
|
|
decoration: globalThemeDecoration,
|
|
dataRowColor: globalThemeDataRowColor,
|
|
dataRowMinHeight: minMaxGlobalThemeDataRowHeight,
|
|
dataRowMaxHeight: minMaxGlobalThemeDataRowHeight,
|
|
dataTextStyle: globalThemeDataTextStyle,
|
|
headingRowColor: globalThemeHeadingRowColor,
|
|
headingRowHeight: globalThemeHeadingRowHeight,
|
|
headingTextStyle: globalThemeHeadingTextStyle,
|
|
horizontalMargin: globalThemeHorizontalMargin,
|
|
columnSpacing: globalThemeColumnSpacing,
|
|
dividerThickness: globalThemeDividerThickness,
|
|
headingCellCursor: globalHeadingCellCursor,
|
|
dataRowCursor: globalDataRowCursor,
|
|
),
|
|
),
|
|
home: Scaffold(
|
|
body: DataTableTheme(
|
|
data: const DataTableThemeData(
|
|
decoration: localThemeDecoration,
|
|
dataRowColor: localThemeDataRowColor,
|
|
dataRowMinHeight: minMaxLocalThemeDataRowHeight,
|
|
dataRowMaxHeight: minMaxLocalThemeDataRowHeight,
|
|
dataTextStyle: localThemeDataTextStyle,
|
|
headingRowColor: localThemeHeadingRowColor,
|
|
headingRowHeight: localThemeHeadingRowHeight,
|
|
headingTextStyle: localThemeHeadingTextStyle,
|
|
horizontalMargin: localThemeHorizontalMargin,
|
|
columnSpacing: localThemeColumnSpacing,
|
|
dividerThickness: localThemeDividerThickness,
|
|
headingCellCursor: localHeadingCellCursor,
|
|
dataRowCursor: localDataRowCursor,
|
|
),
|
|
child: DataTable(
|
|
showCheckboxColumn: false,
|
|
sortColumnIndex: 0,
|
|
columns: <DataColumn>[
|
|
DataColumn(label: const Text('A'), onSort: (int columnIndex, bool ascending) {}),
|
|
const DataColumn(label: Text('B')),
|
|
],
|
|
rows: <DataRow>[
|
|
DataRow(
|
|
onSelectChanged: (bool? selected) {},
|
|
cells: const <DataCell>[DataCell(Text('Data')), DataCell(Text('Data 2'))],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
final Finder tableContainerFinder = find.ancestor(
|
|
of: find.byType(Table),
|
|
matching: find.byType(Container),
|
|
);
|
|
expect(
|
|
tester.widgetList<Container>(tableContainerFinder).first.decoration,
|
|
localThemeDecoration,
|
|
);
|
|
|
|
final TextStyle dataRowTextStyle = tester
|
|
.renderObject<RenderParagraph>(find.text('Data'))
|
|
.text
|
|
.style!;
|
|
expect(dataRowTextStyle.fontSize, localThemeDataTextStyle.fontSize);
|
|
expect(
|
|
_tableRowBoxDecoration(tester: tester, index: 1).color,
|
|
localThemeDataRowColor.resolve(<MaterialState>{}),
|
|
);
|
|
expect(
|
|
_tableRowBoxDecoration(tester: tester, index: 1).border!.top.width,
|
|
localThemeDividerThickness,
|
|
);
|
|
expect(tester.getSize(_findFirstContainerFor('Data')).height, minMaxLocalThemeDataRowHeight);
|
|
|
|
final TextStyle headingRowTextStyle = tester
|
|
.renderObject<RenderParagraph>(find.text('A'))
|
|
.text
|
|
.style!;
|
|
expect(headingRowTextStyle.fontSize, localThemeHeadingTextStyle.fontSize);
|
|
expect(
|
|
_tableRowBoxDecoration(tester: tester, index: 0).color,
|
|
localThemeHeadingRowColor.resolve(<MaterialState>{}),
|
|
);
|
|
|
|
expect(tester.getSize(_findFirstContainerFor('A')).height, localThemeHeadingRowHeight);
|
|
expect(tester.getTopLeft(find.text('A')).dx, localThemeHorizontalMargin);
|
|
expect(
|
|
tester.getTopLeft(find.text('Data 2')).dx - tester.getTopRight(find.text('Data')).dx,
|
|
localThemeColumnSpacing,
|
|
);
|
|
|
|
final TestGesture gesture = await tester.createGesture(
|
|
kind: PointerDeviceKind.mouse,
|
|
pointer: 1,
|
|
);
|
|
await gesture.addPointer(location: tester.getCenter(find.text('A')));
|
|
await tester.pump();
|
|
|
|
expect(
|
|
RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1),
|
|
localHeadingCellCursor.resolve(<MaterialState>{}),
|
|
);
|
|
|
|
await gesture.moveTo(tester.getCenter(find.text('Data')));
|
|
await tester.pump();
|
|
|
|
expect(
|
|
RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1),
|
|
localDataRowCursor.resolve(<MaterialState>{}),
|
|
);
|
|
});
|
|
|
|
testWidgets(
|
|
'Local DataTableTheme can override global DataTableTheme - separate test for deprecated dataRowHeight',
|
|
(WidgetTester tester) async {
|
|
const double globalThemeDataRowHeight = 50.0;
|
|
const double localThemeDataRowHeight = 51.0;
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(
|
|
dataTableTheme: const DataTableThemeData(dataRowHeight: globalThemeDataRowHeight),
|
|
),
|
|
home: Scaffold(
|
|
body: DataTableTheme(
|
|
data: const DataTableThemeData(dataRowHeight: localThemeDataRowHeight),
|
|
child: DataTable(
|
|
sortColumnIndex: 0,
|
|
columns: <DataColumn>[
|
|
DataColumn(label: const Text('A'), onSort: (int columnIndex, bool ascending) {}),
|
|
const DataColumn(label: Text('B')),
|
|
],
|
|
rows: const <DataRow>[
|
|
DataRow(cells: <DataCell>[DataCell(Text('Data')), DataCell(Text('Data 2'))]),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
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}) {
|
|
final Table table = tester.widget(find.byType(Table));
|
|
final TableRow tableRow = table.children[index];
|
|
return tableRow.decoration! as BoxDecoration;
|
|
}
|
|
|
|
// The finder matches with the Container of the cell content, as well as the
|
|
// Container wrapping the whole table. The first one is used to test row
|
|
// heights.
|
|
Finder _findFirstContainerFor(String text) => find.widgetWithText(Container, text).first;
|