Skip to content

Commit c8aa37d

Browse files
authored
Fix for #112403 and b/249091367 (#121615)
Fix monodrag gestures for #112403 and b/249091367
1 parent 0a1af28 commit c8aa37d

File tree

2 files changed

+45
-2
lines changed

2 files changed

+45
-2
lines changed

packages/flutter/lib/src/gestures/monodrag.dart

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,24 @@ abstract class DragGestureRecognizer extends OneSequenceGestureRecognizer {
222222
late OffsetPair _initialPosition;
223223
late OffsetPair _pendingDragOffset;
224224
Duration? _lastPendingEventTimestamp;
225+
226+
/// When asserts are enabled, returns the last tracked pending event timestamp
227+
/// for this recognizer.
228+
///
229+
/// Otherwise, returns null.
230+
///
231+
/// This getter is intended for use in framework unit tests. Applications must
232+
/// not depend on its value.
233+
@visibleForTesting
234+
Duration? get debugLastPendingEventTimestamp {
235+
Duration? lastPendingEventTimestamp;
236+
assert(() {
237+
lastPendingEventTimestamp = _lastPendingEventTimestamp;
238+
return true;
239+
}());
240+
return lastPendingEventTimestamp;
241+
}
242+
225243
// The buttons sent by `PointerDownEvent`. If a `PointerMoveEvent` comes with a
226244
// different set of buttons, the gesture is canceled.
227245
int? _initialButtons;
@@ -363,7 +381,7 @@ abstract class DragGestureRecognizer extends OneSequenceGestureRecognizer {
363381
if (_state != _DragState.accepted) {
364382
_state = _DragState.accepted;
365383
final OffsetPair delta = _pendingDragOffset;
366-
final Duration timestamp = _lastPendingEventTimestamp!;
384+
final Duration? timestamp = _lastPendingEventTimestamp;
367385
final Matrix4? transform = _lastTransform;
368386
final Offset localUpdateDelta;
369387
switch (dragStartBehavior) {
@@ -449,7 +467,7 @@ abstract class DragGestureRecognizer extends OneSequenceGestureRecognizer {
449467
}
450468
}
451469

452-
void _checkStart(Duration timestamp, int pointer) {
470+
void _checkStart(Duration? timestamp, int pointer) {
453471
if (onStart != null) {
454472
final DragStartDetails details = DragStartDetails(
455473
sourceTimeStamp: timestamp,

packages/flutter/test/gestures/monodrag_test.dart

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,31 @@ import 'gesture_tester.dart';
1010
void main() {
1111
TestWidgetsFlutterBinding.ensureInitialized();
1212

13+
test('acceptGesture tolerates a null lastPendingEventTimestamp', () {
14+
// Regression test for https://github.com/flutter/flutter/issues/112403
15+
// and b/249091367
16+
final DragGestureRecognizer recognizer = VerticalDragGestureRecognizer();
17+
const PointerDownEvent event = PointerDownEvent(timeStamp: Duration(days: 10));
18+
19+
expect(recognizer.debugLastPendingEventTimestamp, null);
20+
21+
recognizer.addAllowedPointer(event);
22+
expect(recognizer.debugLastPendingEventTimestamp, event.timeStamp);
23+
24+
// Normal case: acceptGesture called and we have a last timestamp set.
25+
recognizer.acceptGesture(event.pointer);
26+
expect(recognizer.debugLastPendingEventTimestamp, null);
27+
28+
// Reject the gesture to reset state and allow accepting it again.
29+
recognizer.rejectGesture(event.pointer);
30+
expect(recognizer.debugLastPendingEventTimestamp, null);
31+
32+
// Not entirely clear how this can happen, but the bugs mentioned above show
33+
// we can end up in this state empircally.
34+
recognizer.acceptGesture(event.pointer);
35+
expect(recognizer.debugLastPendingEventTimestamp, null);
36+
});
37+
1338
testGesture('do not crash on up event for a pending pointer after winning arena for another pointer', (GestureTester tester) {
1439
// Regression test for https://github.com/flutter/flutter/issues/75061.
1540

0 commit comments

Comments
 (0)