Skip to content

Fix stale _entries and pointer tracking in OneSequenceGestureRecognizer.rejectGesture#187515

Open
ishaq2321 wants to merge 1 commit into
flutter:mainfrom
ishaq2321:fix/rejectGesture-cleanup
Open

Fix stale _entries and pointer tracking in OneSequenceGestureRecognizer.rejectGesture#187515
ishaq2321 wants to merge 1 commit into
flutter:mainfrom
ishaq2321:fix/rejectGesture-cleanup

Conversation

@ishaq2321
Copy link
Copy Markdown
Contributor

Summary

OneSequenceGestureRecognizer.rejectGesture was a no-op ({}), leaving stale _entries and tracked pointers when a recognizer lost the gesture arena. This caused pointer route leaks and particularly affected GestureArenaTeam scenarios where losing team members retained per-pointer state indefinitely.

Fixes #187475
Related #117356

Changes

  • OneSequenceGestureRecognizer.rejectGesture: now calls stopTrackingPointer + removes _entries, marked @mustCallSuper
  • PrimaryPointerGestureRecognizer.rejectGesture: added super.rejectGesture(pointer) + @mustCallSuper
  • DragGestureRecognizer.rejectGesture: added super.rejectGesture(pointer) + @mustCallSuper
  • ForcePressGestureRecognizer.rejectGesture: added super.rejectGesture(pointer) + @mustCallSuper
  • ScaleGestureRecognizer.rejectGesture: added super.rejectGesture(pointer) + @mustCallSuper
  • _TapStatusTrackerMixin.rejectGesture: added super.rejectGesture(pointer) to ensure chain reaches base
  • BaseTapAndDragGestureRecognizer.rejectGesture: added @mustCallSuper
  • PassiveGestureRecognizer (test helper): removed unnecessary override
  • PrimaryPointerGestureRecognizer: state now transitions to ready via didStopTrackingLastPointer after rejection
  • New test: losing team member cleans up per-pointer state in team_test.dart
  • Updated test: PrimaryPointerGestureRecognizer state expectations in recognizer_test.dart

Tests

All 344 gesture tests pass.

…er.rejectGesture

OneSequenceGestureRecognizer.rejectGesture was a no-op, leaving stale
_entries and tracked pointers when a recognizer lost the gesture arena.
This caused issues particularly in GestureArenaTeam scenarios where losing
team members retained per-pointer state indefinitely.

Fix the base class cleanup and propagate it through all subclass overrides:

- OneSequenceGestureRecognizer.rejectGesture: call stopTrackingPointer
  and remove the stale _entries entry. Marked @mustCallSuper.
- PrimaryPointerGestureRecognizer.rejectGesture: add super.rejectGesture
  call and @mustCallSuper.
- DragGestureRecognizer.rejectGesture: add super.rejectGesture call
  (before _giveUpPointer to avoid redundant work).
- ForcePressGestureRecognizer.rejectGesture: add super.rejectGesture call
  and @mustCallSuper.
- ScaleGestureRecognizer.rejectGesture: add super.rejectGesture call
  and @mustCallSuper.
- PassiveGestureRecognizer (test helper): remove unnecessary override
  since the base class now handles cleanup.

Also fix PrimaryPointerGestureRecognizer state transition: after rejection,
didStopTrackingLastPointer now properly transitions to ready state and
clears initialPosition, since the base class cleanup calls through the
stopTrackingPointer -> didStopTrackingLastPointer chain.

Co-authored-by: Muhammad Ishaq Khan <muhammadishaqkhan.2321@gmail.com>
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors OneSequenceGestureRecognizer and its subclasses to mark rejectGesture with @mustCallSuper and ensure it performs pointer cleanup. Feedback points out that BaseTapAndDragGestureRecognizer.rejectGesture fails to call super.rejectGesture, which violates the @mustCallSuper contract. Additionally, several subclasses contain redundant pointer cleanup calls that can be removed because they are now handled by the superclass implementation.

Comment on lines 1131 to 1135
@override
@mustCallSuper
void rejectGesture(int pointer) {
if (pointer != _primaryPointer) {
return;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The BaseTapAndDragGestureRecognizer.rejectGesture method is marked @mustCallSuper but does not actually call super.rejectGesture(pointer). This will trigger a Dart analyzer warning/error and prevent the necessary cleanup of tracked pointers and _entries for this recognizer.

To fix this, ensure super.rejectGesture(pointer) is called for all execution paths:

  1. Call super.rejectGesture(pointer) before the early return when pointer != _primaryPointer.
  2. Call super.rejectGesture(pointer) at the end of the method (or before any other return points) for the primary pointer case.
  @override
  @mustCallSuper
  void rejectGesture(int pointer) {
    if (pointer != _primaryPointer) {
      super.rejectGesture(pointer);
      return;
    }

Comment on lines 829 to 837
@override
@mustCallSuper
void rejectGesture(int pointer) {
_pointerPanZooms.remove(pointer);
_pointerLocations.remove(pointer);
_pointerQueue.remove(pointer);
stopTrackingPointer(pointer);
super.rejectGesture(pointer);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The call to stopTrackingPointer(pointer) is redundant here because super.rejectGesture(pointer) already calls stopTrackingPointer(pointer) internally. We can simplify this method by removing the redundant call.

  @override
  @mustCallSuper
  void rejectGesture(int pointer) {
    _pointerPanZooms.remove(pointer);
    _pointerLocations.remove(pointer);
    _pointerQueue.remove(pointer);
    super.rejectGesture(pointer);
  }

Comment on lines 352 to 358
@override
@mustCallSuper
void rejectGesture(int pointer) {
stopTrackingPointer(pointer);
didStopTrackingLastPointer(pointer);
super.rejectGesture(pointer);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The manual calls to stopTrackingPointer(pointer) and didStopTrackingLastPointer(pointer) are redundant because super.rejectGesture(pointer) already calls stopTrackingPointer(pointer) (which internally triggers didStopTrackingLastPointer when the last pointer is stopped).

You can simplify this to just call super.rejectGesture(pointer). Even better, since this makes the entire override redundant, you can completely remove the rejectGesture method from ForcePressGestureRecognizer.

  @override
  @mustCallSuper
  void rejectGesture(int pointer) {
    super.rejectGesture(pointer);
  }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

f: gestures flutter/packages/flutter/gestures repository. framework flutter/packages/flutter repository. See also f: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant