Add onLongPress property to DataCell: #72609 (#75393)

This commit is contained in:
Rashid-Khabeer 2021-02-10 16:56:04 -08:00 committed by GitHub
parent 705cebb239
commit a178bba5d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 107 additions and 9 deletions

View File

@ -206,6 +206,10 @@ class DataCell {
this.placeholder = false, this.placeholder = false,
this.showEditIcon = false, this.showEditIcon = false,
this.onTap, this.onTap,
this.onLongPress,
this.onTapDown,
this.onDoubleTap,
this.onTapCancel,
}) : assert(child != null); }) : assert(child != null);
/// A cell that has no content and has zero width and height. /// A cell that has no content and has zero width and height.
@ -241,14 +245,48 @@ class DataCell {
/// Called if the cell is tapped. /// Called if the cell is tapped.
/// ///
/// If non-null, tapping the cell will call this callback. If /// If non-null, tapping the cell will call this callback. If
/// null, tapping the cell will attempt to select the row (if /// null (including [onDoubleTap], [onLongPress], [onTapCancel] and [onTapDown]),
/// tapping the cell will attempt to select the row (if
/// [DataRow.onSelectChanged] is provided). /// [DataRow.onSelectChanged] is provided).
/// final GestureTapCallback? onTap;
/// To define a tap behavior for the entire row, see
/// [DataRow.onSelectChanged].
final VoidCallback? onTap;
bool get _debugInteractive => onTap != null; /// Called when the cell is double tapped.
///
/// If non-null, tapping the cell will call this callback. If
/// null (including [onTap], [onLongPress], [onTapCancel] and [onTapDown]),
/// tapping the cell will attempt to select the row (if
/// [DataRow.onSelectChanged] is provided).
final GestureTapCallback? onDoubleTap;
/// Called if the cell is long-pressed.
///
/// If non-null, tapping the cell will invoke this callback. If
/// null (including [onDoubleTap], [onTap], [onTapCancel] and [onTapDown]),
/// tapping the cell will attempt to select the row (if
/// [DataRow.onSelectChanged] is provided).
final GestureLongPressCallback? onLongPress;
/// Called if the cell is tapped down.
///
/// If non-null, tapping the cell will call this callback. If
/// null (including [onTap] [onDoubleTap], [onLongPress] and [onTapCancel]),
/// tapping the cell will attempt to select the row (if
/// [DataRow.onSelectChanged] is provided).
final GestureTapDownCallback? onTapDown;
/// Called if the user cancels a tap was started on cell.
///
/// If non-null, cancelling the tap gesture will invoke this callback.
/// If null (including [onTap], [onDoubleTap] and [onLongPress]),
/// tapping the cell will attempt to select the
/// row (if [DataRow.onSelectChanged] is provided).
final GestureTapCancelCallback? onTapCancel;
bool get _debugInteractive => onTap != null ||
onDoubleTap != null ||
onLongPress != null ||
onTapDown != null ||
onTapCancel != null;
} }
/// A material design data table. /// A material design data table.
@ -809,8 +847,12 @@ class DataTable extends StatelessWidget {
required bool numeric, required bool numeric,
required bool placeholder, required bool placeholder,
required bool showEditIcon, required bool showEditIcon,
required VoidCallback? onTap, required GestureTapCallback? onTap,
required VoidCallback? onSelectChanged, required VoidCallback? onSelectChanged,
required GestureTapCallback? onDoubleTap,
required GestureLongPressCallback? onLongPress,
required GestureTapDownCallback? onTapDown,
required GestureTapCancelCallback? onTapCancel,
required MaterialStateProperty<Color?>? overlayColor, required MaterialStateProperty<Color?>? overlayColor,
}) { }) {
final ThemeData themeData = Theme.of(context); final ThemeData themeData = Theme.of(context);
@ -840,9 +882,17 @@ class DataTable extends StatelessWidget {
child: DropdownButtonHideUnderline(child: label), child: DropdownButtonHideUnderline(child: label),
), ),
); );
if (onTap != null) { if (onTap != null ||
onDoubleTap != null ||
onLongPress != null ||
onTapDown != null ||
onTapCancel != null) {
label = InkWell( label = InkWell(
onTap: onTap, onTap: onTap,
onDoubleTap: onDoubleTap,
onLongPress: onLongPress,
onTapCancel: onTapCancel,
onTapDown: onTapDown,
child: label, child: label,
overlayColor: overlayColor, overlayColor: overlayColor,
); );
@ -1000,6 +1050,10 @@ class DataTable extends StatelessWidget {
placeholder: cell.placeholder, placeholder: cell.placeholder,
showEditIcon: cell.showEditIcon, showEditIcon: cell.showEditIcon,
onTap: cell.onTap, onTap: cell.onTap,
onDoubleTap: cell.onDoubleTap,
onLongPress: cell.onLongPress,
onTapCancel: cell.onTapCancel,
onTapDown: cell.onTapDown,
onSelectChanged: () => row.onSelectChanged != null ? row.onSelectChanged!(!row.selected) : null, onSelectChanged: () => row.onSelectChanged != null ? row.onSelectChanged!(!row.selected) : null,
overlayColor: row.color ?? effectiveDataRowColor, overlayColor: row.color ?? effectiveDataRowColor,
); );

View File

@ -54,6 +54,18 @@ void main() {
onTap: () { onTap: () {
log.add('cell-tap: ${dessert.calories}'); log.add('cell-tap: ${dessert.calories}');
}, },
onDoubleTap: () {
log.add('cell-doubleTap: ${dessert.calories}');
},
onLongPress: () {
log.add('cell-longPress: ${dessert.calories}');
},
onTapCancel: () {
log.add('cell-tapCancel: ${dessert.calories}');
},
onTapDown: (TapDownDetails details) {
log.add('cell-tapDown: ${dessert.calories}');
},
), ),
], ],
); );
@ -94,9 +106,41 @@ void main() {
)); ));
await tester.pumpAndSettle(const Duration(milliseconds: 200)); await tester.pumpAndSettle(const Duration(milliseconds: 200));
await tester.tap(find.text('375'));
await tester.pump(const Duration(milliseconds: 100));
await tester.tap(find.text('375')); await tester.tap(find.text('375'));
expect(log, <String>['cell-tap: 375']); expect(log, <String>['cell-doubleTap: 375']);
log.clear();
await tester.longPress(find.text('375'));
// The tap down is triggered on gesture down.
// Then, the cancel is triggered when the gesture arena
// recognizes that the long press overrides the tap event
// so it triggers a tap cancel, followed by the long press.
expect(log,<String>['cell-tapDown: 375' ,'cell-tapCancel: 375', 'cell-longPress: 375']);
log.clear();
TestGesture gesture = await tester.startGesture(
tester.getRect(find.text('375')).center,
);
await tester.pump(const Duration(milliseconds: 100));
// onTapDown callback is registered.
expect(log, equals(<String>['cell-tapDown: 375']));
await gesture.up();
await tester.pump(const Duration(seconds: 1));
// onTap callback is registered after the gesture is removed.
expect(log, equals(<String>['cell-tapDown: 375', 'cell-tap: 375']));
log.clear();
// dragging off the bounds of the cell calls the cancel callback
gesture = await tester.startGesture(tester.getRect(find.text('375')).center);
await tester.pump(const Duration(milliseconds: 100));
await gesture.moveBy(const Offset(0.0, 200.0));
await gesture.cancel();
expect(log, equals(<String>['cell-tapDown: 375', 'cell-tapCancel: 375']));
log.clear(); log.clear();
await tester.tap(find.byType(Checkbox).last); await tester.tap(find.byType(Checkbox).last);