mirror of
https://github.com/flutter/flutter
synced 2024-08-27 12:00:58 +00:00
Add customizable mouse cursor to DataTable
(#123128)
This commit is contained in:
parent
25e692cb26
commit
f7fb14ec58
|
@ -44,6 +44,7 @@ class DataColumn {
|
|||
this.tooltip,
|
||||
this.numeric = false,
|
||||
this.onSort,
|
||||
this.mouseCursor,
|
||||
});
|
||||
|
||||
/// The column heading.
|
||||
|
@ -85,6 +86,20 @@ class DataColumn {
|
|||
final DataColumnSortCallback? onSort;
|
||||
|
||||
bool get _debugInteractive => onSort != null;
|
||||
|
||||
/// The cursor for a mouse pointer when it enters or is hovering over the
|
||||
/// heading row.
|
||||
///
|
||||
/// [MaterialStateProperty.resolve] is used for the following [MaterialState]s:
|
||||
///
|
||||
/// * [MaterialState.disabled].
|
||||
///
|
||||
/// If this is null, then the value of [DataTableThemeData.headingCellCursor]
|
||||
/// is used. If that's null, then [MaterialStateMouseCursor.clickable] is used.
|
||||
///
|
||||
/// See also:
|
||||
/// * [MaterialStateMouseCursor], which can be used to create a [MouseCursor].
|
||||
final MaterialStateProperty<MouseCursor?>? mouseCursor;
|
||||
}
|
||||
|
||||
/// Row configuration and cell data for a [DataTable].
|
||||
|
@ -106,6 +121,7 @@ class DataRow {
|
|||
this.onSelectChanged,
|
||||
this.onLongPress,
|
||||
this.color,
|
||||
this.mouseCursor,
|
||||
required this.cells,
|
||||
});
|
||||
|
||||
|
@ -119,6 +135,7 @@ class DataRow {
|
|||
this.onSelectChanged,
|
||||
this.onLongPress,
|
||||
this.color,
|
||||
this.mouseCursor,
|
||||
required this.cells,
|
||||
}) : key = ValueKey<int?>(index);
|
||||
|
||||
|
@ -205,6 +222,20 @@ class DataRow {
|
|||
/// <https://material.io/design/interaction/states.html#anatomy>.
|
||||
final MaterialStateProperty<Color?>? color;
|
||||
|
||||
/// The cursor for a mouse pointer when it enters or is hovering over the
|
||||
/// data row.
|
||||
///
|
||||
/// [MaterialStateProperty.resolve] is used for the following [MaterialState]s:
|
||||
///
|
||||
/// * [MaterialState.selected].
|
||||
///
|
||||
/// If this is null, then the value of [DataTableThemeData.dataRowCursor]
|
||||
/// is used. If that's null, then [MaterialStateMouseCursor.clickable] is used.
|
||||
///
|
||||
/// See also:
|
||||
/// * [MaterialStateMouseCursor], which can be used to create a [MouseCursor].
|
||||
final MaterialStateProperty<MouseCursor?>? mouseCursor;
|
||||
|
||||
bool get _debugInteractive => onSelectChanged != null || cells.any((DataCell cell) => cell._debugInteractive);
|
||||
}
|
||||
|
||||
|
@ -738,6 +769,7 @@ class DataTable extends StatelessWidget {
|
|||
required ValueChanged<bool?>? onCheckboxChanged,
|
||||
required MaterialStateProperty<Color?>? overlayColor,
|
||||
required bool tristate,
|
||||
MouseCursor? rowMouseCursor,
|
||||
}) {
|
||||
final ThemeData themeData = Theme.of(context);
|
||||
final double effectiveHorizontalMargin = horizontalMargin
|
||||
|
@ -769,6 +801,7 @@ class DataTable extends StatelessWidget {
|
|||
contents = TableRowInkWell(
|
||||
onTap: onRowTap,
|
||||
overlayColor: overlayColor,
|
||||
mouseCursor: rowMouseCursor,
|
||||
child: contents,
|
||||
);
|
||||
}
|
||||
|
@ -788,6 +821,7 @@ class DataTable extends StatelessWidget {
|
|||
required bool sorted,
|
||||
required bool ascending,
|
||||
required MaterialStateProperty<Color?>? overlayColor,
|
||||
required MouseCursor? mouseCursor,
|
||||
}) {
|
||||
final ThemeData themeData = Theme.of(context);
|
||||
final DataTableThemeData dataTableTheme = DataTableTheme.of(context);
|
||||
|
@ -838,6 +872,7 @@ class DataTable extends StatelessWidget {
|
|||
label = InkWell(
|
||||
onTap: onSort,
|
||||
overlayColor: overlayColor,
|
||||
mouseCursor: mouseCursor,
|
||||
child: label,
|
||||
);
|
||||
return label;
|
||||
|
@ -858,6 +893,7 @@ class DataTable extends StatelessWidget {
|
|||
required GestureTapCancelCallback? onTapCancel,
|
||||
required MaterialStateProperty<Color?>? overlayColor,
|
||||
required GestureLongPressCallback? onRowLongPress,
|
||||
required MouseCursor? mouseCursor,
|
||||
}) {
|
||||
final ThemeData themeData = Theme.of(context);
|
||||
final DataTableThemeData dataTableTheme = DataTableTheme.of(context);
|
||||
|
@ -912,6 +948,7 @@ class DataTable extends StatelessWidget {
|
|||
onTap: onSelectChanged,
|
||||
onLongPress: onRowLongPress,
|
||||
overlayColor: overlayColor,
|
||||
mouseCursor: mouseCursor,
|
||||
child: label,
|
||||
);
|
||||
}
|
||||
|
@ -1014,12 +1051,17 @@ class DataTable extends StatelessWidget {
|
|||
);
|
||||
rowIndex = 1;
|
||||
for (final DataRow row in rows) {
|
||||
final Set<MaterialState> states = <MaterialState>{
|
||||
if (row.selected)
|
||||
MaterialState.selected,
|
||||
};
|
||||
tableRows[rowIndex].children[0] = _buildCheckbox(
|
||||
context: context,
|
||||
checked: row.selected,
|
||||
onRowTap: row.onSelectChanged == null ? null : () => row.onSelectChanged?.call(!row.selected),
|
||||
onCheckboxChanged: row.onSelectChanged,
|
||||
overlayColor: row.color ?? effectiveDataRowColor,
|
||||
rowMouseCursor: row.mouseCursor?.resolve(states) ?? dataTableTheme.dataRowCursor?.resolve(states),
|
||||
tristate: false,
|
||||
);
|
||||
rowIndex += 1;
|
||||
|
@ -1057,6 +1099,10 @@ class DataTable extends StatelessWidget {
|
|||
} else {
|
||||
tableColumns[displayColumnIndex] = const IntrinsicColumnWidth();
|
||||
}
|
||||
final Set<MaterialState> headerStates = <MaterialState>{
|
||||
if (column.onSort == null)
|
||||
MaterialState.disabled,
|
||||
};
|
||||
tableRows[0].children[displayColumnIndex] = _buildHeadingCell(
|
||||
context: context,
|
||||
padding: padding,
|
||||
|
@ -1067,9 +1113,14 @@ class DataTable extends StatelessWidget {
|
|||
sorted: dataColumnIndex == sortColumnIndex,
|
||||
ascending: sortAscending,
|
||||
overlayColor: effectiveHeadingRowColor,
|
||||
mouseCursor: column.mouseCursor?.resolve(headerStates) ?? dataTableTheme.headingCellCursor?.resolve(headerStates),
|
||||
);
|
||||
rowIndex = 1;
|
||||
for (final DataRow row in rows) {
|
||||
final Set<MaterialState> states = <MaterialState>{
|
||||
if (row.selected)
|
||||
MaterialState.selected,
|
||||
};
|
||||
final DataCell cell = row.cells[dataColumnIndex];
|
||||
tableRows[rowIndex].children[displayColumnIndex] = _buildDataCell(
|
||||
context: context,
|
||||
|
@ -1086,6 +1137,7 @@ class DataTable extends StatelessWidget {
|
|||
onSelectChanged: row.onSelectChanged == null ? null : () => row.onSelectChanged?.call(!row.selected),
|
||||
overlayColor: row.color ?? effectiveDataRowColor,
|
||||
onRowLongPress: row.onLongPress,
|
||||
mouseCursor: row.mouseCursor?.resolve(states) ?? dataTableTheme.dataRowCursor?.resolve(states),
|
||||
);
|
||||
rowIndex += 1;
|
||||
}
|
||||
|
@ -1138,6 +1190,7 @@ class TableRowInkWell extends InkResponse {
|
|||
super.onLongPress,
|
||||
super.onHighlightChanged,
|
||||
super.overlayColor,
|
||||
super.mouseCursor,
|
||||
}) : super(
|
||||
containedInkWell: true,
|
||||
highlightShape: BoxShape.rectangle,
|
||||
|
|
|
@ -55,6 +55,8 @@ class DataTableThemeData with Diagnosticable {
|
|||
this.columnSpacing,
|
||||
this.dividerThickness,
|
||||
this.checkboxHorizontalMargin,
|
||||
this.headingCellCursor,
|
||||
this.dataRowCursor,
|
||||
}) : 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.'),
|
||||
|
@ -106,6 +108,12 @@ class DataTableThemeData with Diagnosticable {
|
|||
/// {@macro flutter.material.dataTable.checkboxHorizontalMargin}
|
||||
final double? checkboxHorizontalMargin;
|
||||
|
||||
/// If specified, overrides the default value of [DataColumn.mouseCursor].
|
||||
final MaterialStateProperty<MouseCursor?>? headingCellCursor;
|
||||
|
||||
/// If specified, overrides the default value of [DataRow.mouseCursor].
|
||||
final MaterialStateProperty<MouseCursor?>? dataRowCursor;
|
||||
|
||||
/// Creates a copy of this object but with the given fields replaced with the
|
||||
/// new values.
|
||||
DataTableThemeData copyWith({
|
||||
|
@ -126,6 +134,8 @@ class DataTableThemeData with Diagnosticable {
|
|||
double? columnSpacing,
|
||||
double? dividerThickness,
|
||||
double? checkboxHorizontalMargin,
|
||||
MaterialStateProperty<MouseCursor?>? headingCellCursor,
|
||||
MaterialStateProperty<MouseCursor?>? dataRowCursor,
|
||||
}) {
|
||||
return DataTableThemeData(
|
||||
decoration: decoration ?? this.decoration,
|
||||
|
@ -141,6 +151,8 @@ class DataTableThemeData with Diagnosticable {
|
|||
columnSpacing: columnSpacing ?? this.columnSpacing,
|
||||
dividerThickness: dividerThickness ?? this.dividerThickness,
|
||||
checkboxHorizontalMargin: checkboxHorizontalMargin ?? this.checkboxHorizontalMargin,
|
||||
headingCellCursor: headingCellCursor ?? this.headingCellCursor,
|
||||
dataRowCursor: dataRowCursor ?? this.dataRowCursor,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -166,6 +178,8 @@ class DataTableThemeData with Diagnosticable {
|
|||
columnSpacing: lerpDouble(a.columnSpacing, b.columnSpacing, t),
|
||||
dividerThickness: lerpDouble(a.dividerThickness, b.dividerThickness, t),
|
||||
checkboxHorizontalMargin: lerpDouble(a.checkboxHorizontalMargin, b.checkboxHorizontalMargin, t),
|
||||
headingCellCursor: t < 0.5 ? a.headingCellCursor : b.headingCellCursor,
|
||||
dataRowCursor: t < 0.5 ? a.dataRowCursor : b.dataRowCursor,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -183,6 +197,8 @@ class DataTableThemeData with Diagnosticable {
|
|||
columnSpacing,
|
||||
dividerThickness,
|
||||
checkboxHorizontalMargin,
|
||||
headingCellCursor,
|
||||
dataRowCursor,
|
||||
);
|
||||
|
||||
@override
|
||||
|
@ -205,7 +221,9 @@ class DataTableThemeData with Diagnosticable {
|
|||
&& other.horizontalMargin == horizontalMargin
|
||||
&& other.columnSpacing == columnSpacing
|
||||
&& other.dividerThickness == dividerThickness
|
||||
&& other.checkboxHorizontalMargin == checkboxHorizontalMargin;
|
||||
&& other.checkboxHorizontalMargin == checkboxHorizontalMargin
|
||||
&& other.headingCellCursor == headingCellCursor
|
||||
&& other.dataRowCursor == dataRowCursor;
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -223,6 +241,8 @@ class DataTableThemeData with Diagnosticable {
|
|||
properties.add(DoubleProperty('columnSpacing', columnSpacing, defaultValue: null));
|
||||
properties.add(DoubleProperty('dividerThickness', dividerThickness, defaultValue: null));
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import 'dart:math' as math;
|
|||
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:vector_math/vector_math_64.dart' show Matrix3;
|
||||
|
||||
|
@ -2072,4 +2073,169 @@ void main() {
|
|||
expect(() => createDataTable(dataRowHeight: 1.0, dataRowMinHeight: 2.0), throwsA(predicate((AssertionError e) =>
|
||||
e.toString().contains('dataRowHeight == null || (dataRowMinHeight == null && dataRowMaxHeight == null)'))));
|
||||
});
|
||||
|
||||
testWidgets('Heading cell cursor resolves MaterialStateMouseCursor correctly', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Scaffold(
|
||||
body: DataTable(
|
||||
sortColumnIndex: 0,
|
||||
columns: <DataColumn>[
|
||||
// This column can be sorted.
|
||||
DataColumn(
|
||||
mouseCursor: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
return SystemMouseCursors.forbidden;
|
||||
}
|
||||
return SystemMouseCursors.copy;
|
||||
}),
|
||||
|
||||
onSort: (int columnIndex, bool ascending) {},
|
||||
label: const Text('A'),
|
||||
),
|
||||
// This column cannot be sorted.
|
||||
DataColumn(
|
||||
mouseCursor: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
return SystemMouseCursors.forbidden;
|
||||
}
|
||||
return SystemMouseCursors.copy;
|
||||
}),
|
||||
label: const Text('B'),
|
||||
),
|
||||
],
|
||||
rows: const <DataRow>[
|
||||
DataRow(
|
||||
cells: <DataCell>[
|
||||
DataCell(Text('Data 1')),
|
||||
DataCell(Text('Data 2')),
|
||||
],
|
||||
),
|
||||
DataRow(
|
||||
cells: <DataCell>[
|
||||
DataCell(Text('Data 3')),
|
||||
DataCell(Text('Data 4')),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
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.copy);
|
||||
|
||||
await gesture.moveTo(tester.getCenter(find.text('B')));
|
||||
await tester.pump();
|
||||
|
||||
expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.forbidden);
|
||||
});
|
||||
|
||||
testWidgets('DataRow cursor resolves MaterialStateMouseCursor correctly', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Scaffold(
|
||||
body: DataTable(
|
||||
sortColumnIndex: 0,
|
||||
columns: <DataColumn>[
|
||||
DataColumn(
|
||||
label: const Text('A'),
|
||||
onSort: (int columnIndex, bool ascending) {},
|
||||
),
|
||||
const DataColumn(label: Text('B')),
|
||||
],
|
||||
rows: <DataRow>[
|
||||
// This row can be selected.
|
||||
DataRow(
|
||||
mouseCursor: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.selected)) {
|
||||
return SystemMouseCursors.copy;
|
||||
}
|
||||
return SystemMouseCursors.forbidden;
|
||||
}),
|
||||
onSelectChanged: (bool? selected) {},
|
||||
cells: const <DataCell>[
|
||||
DataCell(Text('Data 1')),
|
||||
DataCell(Text('Data 2')),
|
||||
],
|
||||
),
|
||||
// This row is selected.
|
||||
DataRow(
|
||||
selected: true,
|
||||
onSelectChanged: (bool? selected) {},
|
||||
mouseCursor: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.selected)) {
|
||||
return SystemMouseCursors.copy;
|
||||
}
|
||||
return SystemMouseCursors.forbidden;
|
||||
}),
|
||||
cells: const <DataCell>[
|
||||
DataCell(Text('Data 3')),
|
||||
DataCell(Text('Data 4')),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse, pointer: 1);
|
||||
await gesture.addPointer(location: tester.getCenter(find.text('Data 1')));
|
||||
await tester.pump();
|
||||
|
||||
expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.forbidden);
|
||||
|
||||
await gesture.moveTo(tester.getCenter(find.text('Data 3')));
|
||||
await tester.pump();
|
||||
|
||||
expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.copy);
|
||||
});
|
||||
|
||||
testWidgets("DataRow cursor doesn't update checkbox cursor", (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Scaffold(
|
||||
body: DataTable(
|
||||
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) {},
|
||||
mouseCursor: const MaterialStatePropertyAll<MouseCursor>(SystemMouseCursors.copy),
|
||||
cells: const <DataCell>[
|
||||
DataCell(Text('Data')),
|
||||
DataCell(Text('Data 2')),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse, pointer: 1);
|
||||
await gesture.addPointer(location: tester.getCenter(find.byType(Checkbox).last));
|
||||
await tester.pump();
|
||||
|
||||
// Test that the checkbox cursor is not changed.
|
||||
expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.click);
|
||||
|
||||
await gesture.moveTo(tester.getCenter(find.text('Data')));
|
||||
await tester.pump();
|
||||
|
||||
// Test that cursor is updated for the row.
|
||||
expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.copy);
|
||||
});
|
||||
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// 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';
|
||||
|
@ -32,6 +33,8 @@ void main() {
|
|||
expect(themeData.columnSpacing, null);
|
||||
expect(themeData.dividerThickness, null);
|
||||
expect(themeData.checkboxHorizontalMargin, null);
|
||||
expect(themeData.headingCellCursor, null);
|
||||
expect(themeData.dataRowCursor, null);
|
||||
|
||||
const DataTableTheme theme = DataTableTheme(data: DataTableThemeData(), child: SizedBox());
|
||||
expect(theme.data.decoration, null);
|
||||
|
@ -47,6 +50,8 @@ void main() {
|
|||
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);
|
||||
});
|
||||
|
||||
testWidgets('Default DataTableThemeData debugFillProperties', (WidgetTester tester) async {
|
||||
|
@ -80,6 +85,8 @@ void main() {
|
|||
columnSpacing: 4.0,
|
||||
dividerThickness: 5.0,
|
||||
checkboxHorizontalMargin: 6.0,
|
||||
headingCellCursor: const MaterialStatePropertyAll<MouseCursor>(SystemMouseCursors.grab),
|
||||
dataRowCursor: const MaterialStatePropertyAll<MouseCursor>(SystemMouseCursors.forbidden),
|
||||
).debugFillProperties(builder);
|
||||
|
||||
final List<String> description = builder.properties
|
||||
|
@ -99,6 +106,8 @@ void main() {
|
|||
expect(description[9], 'columnSpacing: 4.0');
|
||||
expect(description[10], 'dividerThickness: 5.0');
|
||||
expect(description[11], 'checkboxHorizontalMargin: 6.0');
|
||||
expect(description[12], 'headingCellCursor: MaterialStatePropertyAll(SystemMouseCursor(grab))');
|
||||
expect(description[13], 'dataRowCursor: MaterialStatePropertyAll(SystemMouseCursor(forbidden))');
|
||||
});
|
||||
|
||||
testWidgets('DataTable is themeable', (WidgetTester tester) async {
|
||||
|
@ -112,6 +121,8 @@ void main() {
|
|||
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(
|
||||
|
@ -128,11 +139,14 @@ void main() {
|
|||
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'),
|
||||
|
@ -140,11 +154,14 @@ void main() {
|
|||
),
|
||||
const DataColumn(label: Text('B')),
|
||||
],
|
||||
rows: const <DataRow>[
|
||||
DataRow(cells: <DataCell>[
|
||||
DataCell(Text('Data')),
|
||||
DataCell(Text('Data 2')),
|
||||
]),
|
||||
rows: <DataRow>[
|
||||
DataRow(
|
||||
cells: const <DataCell>[
|
||||
DataCell(Text('Data')),
|
||||
DataCell(Text('Data 2')),
|
||||
],
|
||||
onSelectChanged: (bool? value) { },
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -167,6 +184,17 @@ void main() {
|
|||
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 {
|
||||
|
@ -214,6 +242,8 @@ void main() {
|
|||
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));
|
||||
|
@ -225,6 +255,8 @@ void main() {
|
|||
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(
|
||||
|
@ -241,10 +273,13 @@ void main() {
|
|||
horizontalMargin: themeHorizontalMargin,
|
||||
columnSpacing: themeColumnSpacing,
|
||||
dividerThickness: themeDividerThickness,
|
||||
headingCellCursor: themeHeadingCellCursor,
|
||||
dataRowCursor: themeDataRowCursor,
|
||||
),
|
||||
),
|
||||
home: Scaffold(
|
||||
body: DataTable(
|
||||
showCheckboxColumn: false,
|
||||
decoration: decoration,
|
||||
dataRowColor: dataRowColor,
|
||||
dataRowMinHeight: minMaxDataRowHeight,
|
||||
|
@ -260,15 +295,20 @@ void main() {
|
|||
columns: <DataColumn>[
|
||||
DataColumn(
|
||||
label: const Text('A'),
|
||||
mouseCursor: headingCellCursor,
|
||||
onSort: (int columnIndex, bool ascending) {},
|
||||
),
|
||||
const DataColumn(label: Text('B')),
|
||||
],
|
||||
rows: const <DataRow>[
|
||||
DataRow(cells: <DataCell>[
|
||||
DataCell(Text('Data')),
|
||||
DataCell(Text('Data 2')),
|
||||
]),
|
||||
rows: <DataRow>[
|
||||
DataRow(
|
||||
mouseCursor: dataRowCursor,
|
||||
onSelectChanged: (bool? selected) {},
|
||||
cells: const <DataCell>[
|
||||
DataCell(Text('Data')),
|
||||
DataCell(Text('Data 2')),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -291,6 +331,17 @@ void main() {
|
|||
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 {
|
||||
|
@ -340,6 +391,8 @@ void main() {
|
|||
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));
|
||||
|
@ -351,6 +404,8 @@ void main() {
|
|||
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(
|
||||
|
@ -367,6 +422,8 @@ void main() {
|
|||
horizontalMargin: globalThemeHorizontalMargin,
|
||||
columnSpacing: globalThemeColumnSpacing,
|
||||
dividerThickness: globalThemeDividerThickness,
|
||||
headingCellCursor: globalHeadingCellCursor,
|
||||
dataRowCursor: globalDataRowCursor,
|
||||
),
|
||||
),
|
||||
home: Scaffold(
|
||||
|
@ -383,8 +440,11 @@ void main() {
|
|||
horizontalMargin: localThemeHorizontalMargin,
|
||||
columnSpacing: localThemeColumnSpacing,
|
||||
dividerThickness: localThemeDividerThickness,
|
||||
headingCellCursor: localHeadingCellCursor,
|
||||
dataRowCursor: localDataRowCursor,
|
||||
),
|
||||
child: DataTable(
|
||||
showCheckboxColumn: false,
|
||||
sortColumnIndex: 0,
|
||||
columns: <DataColumn>[
|
||||
DataColumn(
|
||||
|
@ -393,11 +453,14 @@ void main() {
|
|||
),
|
||||
const DataColumn(label: Text('B')),
|
||||
],
|
||||
rows: const <DataRow>[
|
||||
DataRow(cells: <DataCell>[
|
||||
DataCell(Text('Data')),
|
||||
DataCell(Text('Data 2')),
|
||||
]),
|
||||
rows: <DataRow>[
|
||||
DataRow(
|
||||
onSelectChanged: (bool? selected) {},
|
||||
cells: const <DataCell>[
|
||||
DataCell(Text('Data')),
|
||||
DataCell(Text('Data 2')),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -421,6 +484,17 @@ void main() {
|
|||
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 {
|
||||
|
@ -464,7 +538,6 @@ void main() {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
BoxDecoration _tableRowBoxDecoration({required WidgetTester tester, required int index}) {
|
||||
final Table table = tester.widget(find.byType(Table));
|
||||
final TableRow tableRow = table.children[index];
|
||||
|
|
Loading…
Reference in a new issue