Skip to content

Improve fidelity of multi-pointer drag gestures #38926

Closed
@dkwingsmt

Description

@dkwingsmt

The current implementation of DragGestureRecognizer does not work well when multiple pointers (fingers) are involved.

Consider n pointers in contact with the screen. At one frame pointer i moved by delta(i).

Current Flutter behavior

  • When dragging: delta(combined) = sum of delta(n)
    • This is very wrong. When multiple pointers are moving in the same direction, the scrollable will be moved at double or triple the speed.
  • When a pointer leaves: all remaining pointers still work.

After investigation, I discovered that Android and iOS have different implementation.

Android behavior (pan)
Android seems to maintain a stack of pointers in the order of time of first contact, latest being the stack top.

  • When dragging: delta(combined) = delta(latest pointer)
    • It means that when you're dragging with one finger, and then add another finger, the scrollable is acting as if there is only the 2nd finger.
  • When a pointer leaves: the latest of the remaining pointers works. No end speed.

Android behavior (horizontal or vertical)

  • When dragging: position(combined) = position(latest pointer)
  • When a pointer leaves: the latest of the remaining pointers works.
    • It means that the draggable will jump from the previous pointer to the next pointer.

iOS behavior (pan)

  • When dragging: position(combined) = average(i of n) position(i)
  • When a pointer leaves: all remaining pointers still work
    • It means that the draggable will jump from the previous average position to the next average position.

iOS behavior (horizontal or vertical)

  • When dragging: delta(combined) = max(i of n that are positive) delta(i) - max(i of n that are negative) delta(i)
    • It means that, if two fingers are moving +50 and +10 respectively, it will move +50; if they're moving at +50 and -10 respectively, it will move +40.
  • When a pointer leaves: all pointers stop working (but the gesture is still in "accepted" state)

Challenge

The iOS behavior requires the information of which events belong to the same batch, which is not provided by the current gesture API.

A possible solution is to synthesize it by record the events in a map from pointers to events. When the recognizer sees an event for a pointer that already has an event, all events currently in the map are considered a batch, and the map is cleared.

Metadata

Metadata

Assignees

Labels

P3Issues that are less important to the Flutter projecta: qualityA truly polished experiencec: new featureNothing broken; request for a new capabilityc: proposalA detailed proposal for a change to Flutterf: gesturesflutter/packages/flutter/gestures repository.frameworkflutter/packages/flutter repository. See also f: labels.platform-androidAndroid applications specificallyplatform-iosiOS applications specificallyteam-frameworkOwned by Framework teamtriaged-frameworkTriaged by Framework team

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions