diff --git a/packages/flutter/lib/src/gestures/force_press.dart b/packages/flutter/lib/src/gestures/force_press.dart index ad5e5d48bb..39b00183be 100644 --- a/packages/flutter/lib/src/gestures/force_press.dart +++ b/packages/flutter/lib/src/gestures/force_press.dart @@ -98,6 +98,9 @@ typedef GestureForceInterpolation = double Function(double pressureMin, double p /// force touch functionality, with the exception of the iPhone XR. In addition, /// a small handful of Android devices have this functionality as well. /// +/// Devices with faux screen pressure sensors like the Pixel 2 and 3 will not +/// send any force press related callbacks. +/// /// Reported pressure will always be in the range 0.0 to 1.0, where 1.0 is /// maximum pressure and 0.0 is minimum pressure. If using a custom /// [interpolation] callback, the pressure reported will correspond to that @@ -203,10 +206,19 @@ class ForcePressGestureRecognizer extends OneSequenceGestureRecognizer { @override void addPointer(PointerEvent event) { - startTrackingPointer(event.pointer); - if (_state == _ForceState.ready) { - _state = _ForceState.possible; - _lastPosition = event.position; + assert(event.pressureMax >= 1.0); + // If the device has a maximum pressure of less than or equal to 1, + // indicating a faux pressure sensor on this device or a device without a + // pressure sensor (ie. on a non iOS device) we want do not want any + // callbacks to be called. + if (!(event is PointerUpEvent) && event.pressureMax == 1.0) { + resolve(GestureDisposition.rejected); + } else { + startTrackingPointer(event.pointer); + if (_state == _ForceState.ready) { + _state = _ForceState.possible; + _lastPosition = event.position; + } } } @@ -215,13 +227,20 @@ class ForcePressGestureRecognizer extends OneSequenceGestureRecognizer { assert(_state != _ForceState.ready); // A static pointer with changes in pressure creates PointerMoveEvent events. if (event is PointerMoveEvent || event is PointerDownEvent) { + if (event.pressure > event.pressureMax || event.pressure < event.pressureMin) { + debugPrint( + 'The reported device pressure ' + event.pressure.toString() + + ' is outside of the device pressure range where: ' + + event.pressureMin.toString() + ' <= pressure <= ' + event.pressureMax.toString(), + ); + } + final double pressure = interpolation(event.pressureMin, event.pressureMax, event.pressure); assert( - event.pressure < event.pressureMin || // contract is undefined for underflowing pressures... - event.pressure > event.pressureMax || // contract is undefined for overflowing pressures... - pressure.isNaN || // and interpolation may return NaN for values it doesn't want to support... - (pressure <= 1.0 && pressure >= 0.0) // but if everything is going well, it must be in the range 1.0..0.0. + (pressure >= 0.0 && pressure <= 1.0) || // Interpolated pressure must be between 1.0 and 0.0... + pressure.isNaN // and interpolation may return NaN for values it doesn't want to support... ); + _lastPosition = event.position; _lastPressure = pressure; @@ -254,7 +273,7 @@ class ForcePressGestureRecognizer extends OneSequenceGestureRecognizer { ))); } } - if (onUpdate != null && + if (onUpdate != null && !pressure.isNaN && (_state == _ForceState.started || _state == _ForceState.peaked)) { if (onUpdate != null) { invokeCallback('onUpdate', () => onUpdate(ForcePressDetails( @@ -306,7 +325,13 @@ class ForcePressGestureRecognizer extends OneSequenceGestureRecognizer { static double _inverseLerp(double min, double max, double t) { assert(min <= max); - return (t - min) / (max - min); + double value = (t - min) / (max - min); + + // If the device incorrectly reports a pressure outside of pressureMin + // and pressureMax, we still want this recognizer to respond normally. + if (!value.isNaN) + value = value.clamp(0.0, 1.0); + return value; } @override diff --git a/packages/flutter/test/cupertino/text_field_test.dart b/packages/flutter/test/cupertino/text_field_test.dart index db12b4cfb8..1d66b13568 100644 --- a/packages/flutter/test/cupertino/text_field_test.dart +++ b/packages/flutter/test/cupertino/text_field_test.dart @@ -1245,9 +1245,17 @@ void main() { final Offset textfieldStart = tester.getTopLeft(find.byType(CupertinoTextField)); const int pointerValue = 1; - final TestGesture gesture = - await tester.startGesture(textfieldStart + const Offset(150.0, 5.0)); - await gesture.updateWithCustomEvent(PointerMoveEvent(pointer: pointerValue, position: textfieldStart + const Offset(150.0, 5.0), pressure: 0.5, pressureMin: 0, pressureMax: 1)); + final TestGesture gesture = await tester.createGesture(); + await gesture.downWithCustomEvent( + textfieldStart + const Offset(150.0, 5.0), + PointerDownEvent( + pointer: pointerValue, + position: textfieldStart + const Offset(150.0, 5.0), + pressure: 3.0, + pressureMax: 6.0, + pressureMin: 0.0 + ), + ); // We expect the force press to select a word at the given location. expect( controller.selection, diff --git a/packages/flutter/test/gestures/force_press_test.dart b/packages/flutter/test/gestures/force_press_test.dart index 7ea098fb99..8cfb830ee5 100644 --- a/packages/flutter/test/gestures/force_press_test.dart +++ b/packages/flutter/test/gestures/force_press_test.dart @@ -109,6 +109,99 @@ void main() { expect(ended, 1); }); + testGesture('Force presses are not recognized on devices with low maxmium pressure', (GestureTester tester) { + // Device specific constants that represent those from the iPhone X + const double pressureMin = 0; + const double pressureMax = 1.0; + + // Interpolated Flutter pressure values. + const double startPressure = 0.4; // = Device pressure of 2.66. + const double peakPressure = 0.85; // = Device pressure of 5.66. + + int started = 0; + int peaked = 0; + int updated = 0; + int ended = 0; + + void onStart(ForcePressDetails details) { + started += 1; + } + + final ForcePressGestureRecognizer force = ForcePressGestureRecognizer(startPressure: startPressure, peakPressure: peakPressure); + + force.onStart = onStart; + force.onPeak = (ForcePressDetails details) => peaked += 1; + force.onUpdate = (ForcePressDetails details) => updated += 1; + force.onEnd = (ForcePressDetails details) => ended += 1; + + const int pointerValue = 1; + final TestPointer pointer = TestPointer(pointerValue); + const PointerDownEvent down = PointerDownEvent(pointer: pointerValue, position: Offset(10.0, 10.0), pressure: 0, pressureMin: pressureMin, pressureMax: pressureMax); + pointer.setDownInfo(down, const Offset(10.0, 10.0)); + force.addPointer(down); + tester.closeArena(pointerValue); + + expect(started, 0); + expect(peaked, 0); + expect(updated, 0); + expect(ended, 0); + + // Pressure fed into the test environment simulates the values received directly from the device. + tester.route(const PointerMoveEvent(pointer: pointerValue, position: Offset(10.0, 10.0), pressure: 2.5, pressureMin: pressureMin, pressureMax: pressureMax)); + + // We have not hit the start pressure, so no events should be true. + expect(started, 0); + expect(peaked, 0); + expect(updated, 0); + expect(ended, 0); + + tester.route(const PointerMoveEvent(pointer: pointerValue, position: Offset(10.0, 10.0), pressure: 2.8, pressureMin: pressureMin, pressureMax: pressureMax)); + + // We have just hit the start pressure so just the start event should be triggered and one update call should have occurred. + expect(started, 0); + expect(peaked, 0); + expect(updated, 0); + expect(ended, 0); + + tester.route(const PointerMoveEvent(pointer: pointerValue, position: Offset(10.0, 10.0), pressure: 3.3, pressureMin: pressureMin, pressureMax: pressureMax)); + tester.route(const PointerMoveEvent(pointer: pointerValue, position: Offset(10.0, 10.0), pressure: 4.0, pressureMin: pressureMin, pressureMax: pressureMax)); + tester.route(const PointerMoveEvent(pointer: pointerValue, position: Offset(10.0, 10.0), pressure: 5.0, pressureMin: pressureMin, pressureMax: pressureMax)); + tester.route(const PointerMoveEvent(pointer: pointerValue, position: Offset(10.0, 10.0), pressure: 1.0, pressureMin: pressureMin, pressureMax: pressureMax)); + + // We have exceeded the start pressure so update should be greater than 0. + expect(started, 0); + expect(updated, 0); + expect(peaked, 0); + expect(ended, 0); + + tester.route(const PointerMoveEvent(pointer: pointerValue, position: Offset(10.0, 10.0), pressure: 6.0, pressureMin: pressureMin, pressureMax: pressureMax)); + + // We have exceeded the peak pressure so peak pressure should be true. + expect(started, 0); + expect(updated, 0); + expect(peaked, 0); + expect(ended, 0); + + tester.route(const PointerMoveEvent(pointer: pointerValue, position: Offset(10.0, 10.0), pressure: 3.3, pressureMin: pressureMin, pressureMax: pressureMax)); + tester.route(const PointerMoveEvent(pointer: pointerValue, position: Offset(10.0, 10.0), pressure: 4.0, pressureMin: pressureMin, pressureMax: pressureMax)); + tester.route(const PointerMoveEvent(pointer: pointerValue, position: Offset(10.0, 10.0), pressure: 5.0, pressureMin: pressureMin, pressureMax: pressureMax)); + tester.route(const PointerMoveEvent(pointer: pointerValue, position: Offset(10.0, 10.0), pressure: 1.0, pressureMin: pressureMin, pressureMax: pressureMax)); + + // Update is still called. + expect(started, 0); + expect(updated, 0); + expect(peaked, 0); + expect(ended, 0); + + tester.route(pointer.up()); + + // We have ended the gesture so ended should be true. + expect(started, 0); + expect(updated, 0); + expect(peaked, 0); + expect(ended, 0); + }); + testGesture('If minimum pressure is not reached, start and end callbacks are not called', (GestureTester tester) { // Device specific constants that represent those from the iPhone X const double pressureMin = 0; @@ -449,4 +542,110 @@ void main() { expect(peaked, 1); expect(ended, 1); }); + + testGesture('A pressure outside of the device reported min and max pressure will not give an error', (GestureTester tester) { + // Device specific constants that represent those from the iPhone X + const double pressureMin = 0; + const double pressureMax = 6.66; + + // Interpolated Flutter pressure values. + const double startPressure = 0.4; // = Device pressure of 2.66. + const double peakPressure = 0.85; // = Device pressure of 5.66. + + int started = 0; + int peaked = 0; + int updated = 0; + int ended = 0; + + final ForcePressGestureRecognizer force = ForcePressGestureRecognizer(startPressure: startPressure, peakPressure: peakPressure); + + force.onStart = (_) => started += 1; + force.onPeak = (_) => peaked += 1; + force.onUpdate = (_) => updated += 1; + force.onEnd = (_) => ended += 1; + + const int pointerValue = 1; + final TestPointer pointer = TestPointer(pointerValue); + const PointerDownEvent down = PointerDownEvent(pointer: pointerValue, position: Offset(10.0, 10.0), pressure: 0, pressureMin: pressureMin, pressureMax: pressureMax); + pointer.setDownInfo(down, const Offset(10.0, 10.0)); + force.addPointer(down); + tester.closeArena(1); + + expect(started, 0); + expect(peaked, 0); + expect(updated, 0); + expect(ended, 0); + + // Pressure fed into the test environment simulates the values received directly from the device. + tester.route(const PointerMoveEvent(pointer: pointerValue, position: Offset(10.0, 10.0), pressure: 2.5, pressureMin: pressureMin, pressureMax: pressureMax)); + + // We have not hit the start pressure, so no events should be true. + expect(started, 0); + expect(peaked, 0); + expect(updated, 0); + expect(ended, 0); + + // If the case where the pressure is greater than the max pressure were not handled correctly, this move event would throw an error. + tester.route(const PointerMoveEvent(pointer: pointerValue, position: Offset(10.0, 10.0), pressure: 8.0, pressureMin: pressureMin, pressureMax: pressureMax)); + tester.route(pointer.up()); + + expect(started, 1); + expect(peaked, 1); + expect(updated, 1); + expect(ended, 1); + }); + + testGesture('A pressure of NAN will not give an error', (GestureTester tester) { + // Device specific constants that represent those from the iPhone X + const double pressureMin = 0; + const double pressureMax = 6.66; + + // Interpolated Flutter pressure values. + const double startPressure = 0.4; // = Device pressure of 2.66. + const double peakPressure = 0.85; // = Device pressure of 5.66. + + int started = 0; + int peaked = 0; + int updated = 0; + int ended = 0; + + final ForcePressGestureRecognizer force = ForcePressGestureRecognizer(startPressure: startPressure, peakPressure: peakPressure); + + force.onStart = (_) => started += 1; + force.onPeak = (_) => peaked += 1; + force.onUpdate = (_) => updated += 1; + force.onEnd = (_) => ended += 1; + + const int pointerValue = 1; + final TestPointer pointer = TestPointer(pointerValue); + const PointerDownEvent down = PointerDownEvent(pointer: pointerValue, position: Offset(10.0, 10.0), pressure: 0, pressureMin: pressureMin, pressureMax: pressureMax); + pointer.setDownInfo(down, const Offset(10.0, 10.0)); + force.addPointer(down); + tester.closeArena(1); + + expect(started, 0); + expect(peaked, 0); + expect(updated, 0); + expect(ended, 0); + + // Pressure fed into the test environment simulates the values received directly from the device. + tester.route(const PointerMoveEvent(pointer: pointerValue, position: Offset(10.0, 10.0), pressure: 2.5, pressureMin: pressureMin, pressureMax: pressureMax)); + + // We have not hit the start pressure, so no events should be true. + expect(started, 0); + expect(peaked, 0); + expect(updated, 0); + expect(ended, 0); + + tester.route(const PointerMoveEvent(pointer: pointerValue, position: Offset(10.0, 10.0), pressure: 6.0, pressureMin: pressureMin, pressureMax: pressureMax)); + expect(peaked, 1); + + tester.route(const PointerMoveEvent(pointer: pointerValue, position: Offset(10.0, 10.0), pressure: 0.0 / 0.0, pressureMin: pressureMin, pressureMax: pressureMax)); + tester.route(pointer.up()); + + expect(started, 1); + expect(peaked, 1); + expect(updated, 1); + expect(ended, 1); + }); } diff --git a/packages/flutter/test/material/text_field_test.dart b/packages/flutter/test/material/text_field_test.dart index c9e23cdb94..3821f0eac0 100644 --- a/packages/flutter/test/material/text_field_test.dart +++ b/packages/flutter/test/material/text_field_test.dart @@ -2190,7 +2190,6 @@ void main() { expect(controller.selection.extentOffset - controller.selection.baseOffset, 0); }); - testWidgets('Down and up test 2', (WidgetTester tester) async{ await tester.pumpWidget(setupWidget()); const String testValue = 'a big house\njumped over a mouse\nOne more line yay'; // 11 \n 19 @@ -4352,12 +4351,21 @@ void main() { ), ); - final Offset textfieldStart = tester.getTopLeft(find.byType(TextField)); + final Offset offset = tester.getTopLeft(find.byType(TextField)) + const Offset(150.0, 5.0); const int pointerValue = 1; - final TestGesture gesture = - await tester.startGesture(textfieldStart + const Offset(150.0, 5.0)); - await gesture.updateWithCustomEvent(PointerMoveEvent(pointer: pointerValue, position: textfieldStart + const Offset(150.0, 5.0), pressure: 0.5, pressureMin: 0, pressureMax: 1)); + final TestGesture gesture = await tester.createGesture(); + await gesture.downWithCustomEvent( + offset, + PointerDownEvent( + pointer: pointerValue, + position: offset, + pressure: 0.0, + pressureMax: 6.0, + pressureMin: 0.0 + ), + ); + await gesture.updateWithCustomEvent(PointerMoveEvent(pointer: pointerValue, position: offset + const Offset(150.0, 5.0), pressure: 0.5, pressureMin: 0, pressureMax: 1)); // We don't want this gesture to select any word on Android. expect(controller.selection, const TextSelection.collapsed(offset: -1)); @@ -4386,8 +4394,19 @@ void main() { final Offset textfieldStart = tester.getTopLeft(find.byType(CupertinoTextField)); const int pointerValue = 1; - final TestGesture gesture = - await tester.startGesture(textfieldStart + const Offset(150.0, 5.0)); + final Offset offset = textfieldStart + const Offset(150.0, 5.0); + final TestGesture gesture = await tester.createGesture(); + await gesture.downWithCustomEvent( + offset, + PointerDownEvent( + pointer: pointerValue, + position: offset, + pressure: 0.0, + pressureMax: 6.0, + pressureMin: 0.0 + ), + ); + await gesture.updateWithCustomEvent(PointerMoveEvent(pointer: pointerValue, position: textfieldStart + const Offset(150.0, 5.0), pressure: 0.5, pressureMin: 0, pressureMax: 1)); // We expect the force press to select a word at the given location. expect( diff --git a/packages/flutter/test/widgets/gesture_detector_test.dart b/packages/flutter/test/widgets/gesture_detector_test.dart index 922126bf9a..714864b46b 100644 --- a/packages/flutter/test/widgets/gesture_detector_test.dart +++ b/packages/flutter/test/widgets/gesture_detector_test.dart @@ -8,6 +8,8 @@ import 'package:flutter/rendering.dart'; import 'package:flutter/gestures.dart'; void main() { + const Offset forcePressOffset = Offset(400.0, 50.0); + testWidgets('Uncontested scrolls start immediately', (WidgetTester tester) async { bool didStartDrag = false; double updatedDragDelta; @@ -367,9 +369,20 @@ void main() { ), ), ); - - final TestGesture gesture = await tester.startGesture(const Offset(400.0, 50.0)); const int pointerValue = 1; + + final TestGesture gesture = await tester.createGesture(); + await gesture.downWithCustomEvent( + forcePressOffset, + const PointerDownEvent( + pointer: pointerValue, + position: forcePressOffset, + pressure: 0.0, + pressureMax: 6.0, + pressureMin: 0.0 + ), + ); + await gesture.updateWithCustomEvent(const PointerMoveEvent(pointer: pointerValue, position: Offset(0.0, 0.0), pressure: 0.3, pressureMin: 0, pressureMax: 1)); expect(forcePressStart, 0); @@ -428,9 +441,23 @@ void main() { ), ); - final TestGesture gesture = await tester.startGesture(const Offset(400.0, 50.0)); const int pointerValue = 1; - await gesture.updateWithCustomEvent(const PointerMoveEvent(pointer: pointerValue, position: Offset(400.0, 50.0), pressure: 0.3, pressureMin: 0, pressureMax: 1)); + const double maxPressure = 6.0; + + final TestGesture gesture = await tester.createGesture(); + + await gesture.downWithCustomEvent( + forcePressOffset, + const PointerDownEvent( + pointer: pointerValue, + position: forcePressOffset, + pressure: 0.0, + pressureMax: maxPressure, + pressureMin: 0.0 + ), + ); + + await gesture.updateWithCustomEvent(const PointerMoveEvent(pointer: pointerValue, position: Offset(400.0, 50.0), pressure: 0.3, pressureMin: 0, pressureMax: maxPressure)); expect(forcePressStart, 0); expect(longPressTimes, 0); @@ -442,7 +469,7 @@ void main() { expect(forcePressStart, 0); // Failed attempt to trigger the force press. - await gesture.updateWithCustomEvent(const PointerMoveEvent(pointer: pointerValue, position: Offset(400.0, 50.0), pressure: 0.5, pressureMin: 0, pressureMax: 1)); + await gesture.updateWithCustomEvent(const PointerMoveEvent(pointer: pointerValue, position: Offset(400.0, 50.0), pressure: 0.5, pressureMin: 0, pressureMax: maxPressure)); expect(longPressTimes, 1); expect(forcePressStart, 0); @@ -467,8 +494,21 @@ void main() { ), ); - final TestGesture gesture = await tester.startGesture(const Offset(50.0, 50.0)); const int pointerValue = 1; + + final TestGesture gesture = await tester.createGesture(); + + await gesture.downWithCustomEvent( + forcePressOffset, + const PointerDownEvent( + pointer: pointerValue, + position: forcePressOffset, + pressure: 0.0, + pressureMax: 6.0, + pressureMin: 0.0 + ), + ); + await gesture.updateWithCustomEvent(const PointerMoveEvent(pointer: pointerValue, position: Offset(0.0, 0.0), pressure: 0.3, pressureMin: 0, pressureMax: 1)); expect(forcePressStart, 0); diff --git a/packages/flutter/test/widgets/text_selection_test.dart b/packages/flutter/test/widgets/text_selection_test.dart index 084c1ba121..0ca260f6c0 100644 --- a/packages/flutter/test/widgets/text_selection_test.dart +++ b/packages/flutter/test/widgets/text_selection_test.dart @@ -13,6 +13,8 @@ void main() { int doubleTapDownCount; int forcePressStartCount; int forcePressEndCount; + const Offset forcePressOffset = Offset(400.0, 50.0); + void _handleTapDown(TapDownDetails details) { tapCount++; } void _handleSingleTapUp(TapUpDetails details) { singleTapUpCount++; } @@ -155,22 +157,62 @@ void main() { await pumpGestureDetector(tester); const int pointerValue = 1; - TestGesture gesture = await tester.startGesture(const Offset(400.0, 50.0), pointer: pointerValue); + + final TestGesture gesture = await tester.createGesture(); + + await gesture.downWithCustomEvent( + forcePressOffset, + const PointerDownEvent( + pointer: pointerValue, + position: forcePressOffset, + pressure: 0.0, + pressureMax: 6.0, + pressureMin: 0.0 + ), + ); + await gesture.updateWithCustomEvent(const PointerMoveEvent(pointer: pointerValue, position: Offset(0.0, 0.0), pressure: 0.5, pressureMin: 0, pressureMax: 1)); await gesture.up(); await tester.pumpAndSettle(); - gesture = await tester.startGesture(const Offset(400.0, 50.0), pointer: pointerValue); + await gesture.downWithCustomEvent( + forcePressOffset, + const PointerDownEvent( + pointer: pointerValue, + position: forcePressOffset, + pressure: 0.0, + pressureMax: 6.0, + pressureMin: 0.0 + ), + ); await gesture.updateWithCustomEvent(const PointerMoveEvent(pointer: pointerValue, position: Offset(0.0, 0.0), pressure: 0.5, pressureMin: 0, pressureMax: 1)); await gesture.up(); await tester.pump(const Duration(milliseconds: 20)); - gesture = await tester.startGesture(const Offset(400.0, 50.0), pointer: pointerValue); + await gesture.downWithCustomEvent( + forcePressOffset, + const PointerDownEvent( + pointer: pointerValue, + position: forcePressOffset, + pressure: 0.0, + pressureMax: 6.0, + pressureMin: 0.0 + ), + ); await gesture.updateWithCustomEvent(const PointerMoveEvent(pointer: pointerValue, position: Offset(0.0, 0.0), pressure: 0.5, pressureMin: 0, pressureMax: 1)); await gesture.up(); await tester.pump(const Duration(milliseconds: 20)); - gesture = await tester.startGesture(const Offset(400.0, 50.0), pointer: pointerValue); + await gesture.downWithCustomEvent( + forcePressOffset, + const PointerDownEvent( + pointer: pointerValue, + position: forcePressOffset, + pressure: 0.0, + pressureMax: 6.0, + pressureMin: 0.0 + ), + ); await gesture.updateWithCustomEvent(const PointerMoveEvent(pointer: pointerValue, position: Offset(0.0, 0.0), pressure: 0.5, pressureMin: 0, pressureMax: 1)); await gesture.up(); @@ -181,16 +223,49 @@ void main() { await pumpGestureDetector(tester); const int pointerValue = 1; - TestGesture gesture = await tester.startGesture(const Offset(400.0, 50.0), pointer: pointerValue); + final TestGesture gesture = await tester.createGesture(); + await gesture.downWithCustomEvent( + forcePressOffset, + const PointerDownEvent( + pointer: pointerValue, + position: forcePressOffset, + pressure: 0.0, + pressureMax: 6.0, + pressureMin: 0.0 + ), + ); // Initiate a quick tap. - await gesture.updateWithCustomEvent(const PointerMoveEvent(pointer: pointerValue, position: Offset(0.0, 0.0), pressure: 0.0, pressureMin: 0, pressureMax: 1)); + await gesture.updateWithCustomEvent( + const PointerMoveEvent( + pointer: pointerValue, + position: Offset(0.0, 0.0), + pressure: 0.0, + pressureMin: 0, + pressureMax: 1 + ) + ); await tester.pump(const Duration(milliseconds: 50)); await gesture.up(); // Initiate a force tap. - gesture = await tester.startGesture(const Offset(400.0, 50.0), pointer: pointerValue); - await gesture.updateWithCustomEvent(const PointerMoveEvent(pointer: pointerValue, position: Offset(0.0, 0.0), pressure: 0.5, pressureMin: 0, pressureMax: 1)); + await gesture.downWithCustomEvent( + forcePressOffset, + const PointerDownEvent( + pointer: pointerValue, + position: forcePressOffset, + pressure: 0.0, + pressureMax: 6.0, + pressureMin: 0.0 + ), + ); + await gesture.updateWithCustomEvent(const PointerMoveEvent( + pointer: pointerValue, + position: Offset(0.0, 0.0), + pressure: 0.5, + pressureMin: 0, + pressureMax: 1 + )); expect(forcePressStartCount, 1); await tester.pump(const Duration(milliseconds: 50)); diff --git a/packages/flutter_test/lib/src/test_pointer.dart b/packages/flutter_test/lib/src/test_pointer.dart index 10f45f10b0..69ec5c5599 100644 --- a/packages/flutter_test/lib/src/test_pointer.dart +++ b/packages/flutter_test/lib/src/test_pointer.dart @@ -223,6 +223,16 @@ class TestGesture { }); } + /// Dispatch a pointer down event at the given `downLocation`, caching the + /// hit test result with a custom down event. + Future downWithCustomEvent(Offset downLocation, PointerDownEvent event) async { + _pointer.setDownInfo(event, downLocation); + return TestAsyncUtils.guard(() async { + _result = _hitTester(downLocation); + return _dispatcher(event, _result); + }); + } + final EventDispatcher _dispatcher; final HitTester _hitTester; final TestPointer _pointer;