🚀 Implements globalPosition
and localPosition
for TapDragEndDetails
(#159962)
Resolves #159961 ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
This commit is contained in:
parent
a513498487
commit
188d1e1999
@ -373,11 +373,14 @@ class TapDragEndDetails with Diagnosticable {
|
||||
this.velocity = Velocity.zero,
|
||||
this.primaryVelocity,
|
||||
required this.consecutiveTapCount,
|
||||
this.globalPosition = Offset.zero,
|
||||
Offset? localPosition,
|
||||
}) : assert(
|
||||
primaryVelocity == null ||
|
||||
primaryVelocity == velocity.pixelsPerSecond.dx ||
|
||||
primaryVelocity == velocity.pixelsPerSecond.dy,
|
||||
);
|
||||
),
|
||||
localPosition = localPosition ?? globalPosition;
|
||||
|
||||
/// The velocity the pointer was moving when it stopped contacting the screen.
|
||||
///
|
||||
@ -400,12 +403,28 @@ class TapDragEndDetails with Diagnosticable {
|
||||
/// the number in the series this tap is.
|
||||
final int consecutiveTapCount;
|
||||
|
||||
/// The global position at which the pointer lifted from the screen.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [localPosition], which is the [globalPosition] transformed to the
|
||||
/// coordinate space of the event receiver.
|
||||
final Offset globalPosition;
|
||||
|
||||
/// The local position in the coordinate system of the event receiver at which
|
||||
/// the pointer lifted from the screen.
|
||||
///
|
||||
/// Defaults to [globalPosition] if not specified in the constructor.
|
||||
final Offset localPosition;
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
properties.add(DiagnosticsProperty<Velocity>('velocity', velocity));
|
||||
properties.add(DiagnosticsProperty<double?>('primaryVelocity', primaryVelocity));
|
||||
properties.add(DiagnosticsProperty<int>('consecutiveTapCount', consecutiveTapCount));
|
||||
properties.add(DiagnosticsProperty<Offset>('globalPosition', globalPosition));
|
||||
properties.add(DiagnosticsProperty<Offset>('localPosition', localPosition));
|
||||
}
|
||||
}
|
||||
|
||||
@ -900,9 +919,9 @@ sealed class BaseTapAndDragGestureRecognizer extends OneSequenceGestureRecognize
|
||||
_DragState _dragState = _DragState.ready;
|
||||
PointerEvent? _start;
|
||||
late OffsetPair _initialPosition;
|
||||
late OffsetPair _currentPosition;
|
||||
late double _globalDistanceMoved;
|
||||
late double _globalDistanceMovedAllAxes;
|
||||
OffsetPair? _correctedPosition;
|
||||
|
||||
// For drag update throttle.
|
||||
TapDragUpdateDetails? _lastDragUpdateDetails;
|
||||
@ -962,6 +981,7 @@ sealed class BaseTapAndDragGestureRecognizer extends OneSequenceGestureRecognize
|
||||
_globalDistanceMovedAllAxes = 0.0;
|
||||
_dragState = _DragState.possible;
|
||||
_initialPosition = OffsetPair(global: event.position, local: event.localPosition);
|
||||
_currentPosition = _initialPosition;
|
||||
_deadlineTimer = Timer(_deadline, () => _didExceedDeadlineWithEvent(event));
|
||||
}
|
||||
}
|
||||
@ -1089,6 +1109,7 @@ sealed class BaseTapAndDragGestureRecognizer extends OneSequenceGestureRecognize
|
||||
_pastSlopTolerance || _getGlobalDistance(event, _initialPosition) > computedSlop;
|
||||
|
||||
if (_dragState == _DragState.accepted) {
|
||||
_currentPosition = OffsetPair.fromEventPosition(event);
|
||||
_checkDragUpdate(event);
|
||||
} else if (_dragState == _DragState.possible) {
|
||||
if (_start == null) {
|
||||
@ -1141,27 +1162,31 @@ sealed class BaseTapAndDragGestureRecognizer extends OneSequenceGestureRecognize
|
||||
String get debugDescription => 'tap_and_drag';
|
||||
|
||||
void _acceptDrag(PointerEvent event) {
|
||||
assert(_dragState == _DragState.accepted);
|
||||
|
||||
if (!_wonArenaForPrimaryPointer) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (dragStartBehavior == DragStartBehavior.start) {
|
||||
_initialPosition =
|
||||
_initialPosition + OffsetPair(global: event.delta, local: event.localDelta);
|
||||
_initialPosition += OffsetPair(global: event.delta, local: event.localDelta);
|
||||
_currentPosition = _initialPosition;
|
||||
}
|
||||
_checkDragStart(event);
|
||||
if (event.localDelta != Offset.zero) {
|
||||
final Matrix4? localToGlobal =
|
||||
event.transform != null ? Matrix4.tryInvert(event.transform!) : null;
|
||||
final Offset correctedLocalPosition = _initialPosition.local + event.localDelta;
|
||||
final Offset localDelta = event.localDelta;
|
||||
if (localDelta != Offset.zero) {
|
||||
_currentPosition = OffsetPair.fromEventPosition(event);
|
||||
final Offset correctedLocalPosition = _initialPosition.local + localDelta;
|
||||
final Matrix4? localToGlobalTransform =
|
||||
event.transform == null ? null : Matrix4.tryInvert(event.transform!);
|
||||
final Offset globalUpdateDelta = PointerEvent.transformDeltaViaPositions(
|
||||
transform: localToGlobalTransform,
|
||||
untransformedDelta: localDelta,
|
||||
untransformedEndPosition: correctedLocalPosition,
|
||||
untransformedDelta: event.localDelta,
|
||||
transform: localToGlobal,
|
||||
);
|
||||
final OffsetPair updateDelta = OffsetPair(local: event.localDelta, global: globalUpdateDelta);
|
||||
_correctedPosition = _initialPosition + updateDelta; // Only adds delta for down behaviour
|
||||
_checkDragUpdate(event);
|
||||
_correctedPosition = null;
|
||||
final OffsetPair updateDelta = OffsetPair(local: localDelta, global: globalUpdateDelta);
|
||||
// Only adds delta for down behaviour
|
||||
_checkDragUpdate(event, corrected: _initialPosition + updateDelta);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1253,11 +1278,9 @@ sealed class BaseTapAndDragGestureRecognizer extends OneSequenceGestureRecognize
|
||||
_start = null;
|
||||
}
|
||||
|
||||
void _checkDragUpdate(PointerEvent event) {
|
||||
final Offset globalPosition =
|
||||
_correctedPosition != null ? _correctedPosition!.global : event.position;
|
||||
final Offset localPosition =
|
||||
_correctedPosition != null ? _correctedPosition!.local : event.localPosition;
|
||||
void _checkDragUpdate(PointerEvent event, {OffsetPair? corrected}) {
|
||||
final Offset globalPosition = corrected?.global ?? event.position;
|
||||
final Offset localPosition = corrected?.local ?? event.localPosition;
|
||||
|
||||
final TapDragUpdateDetails details = TapDragUpdateDetails(
|
||||
sourceTimeStamp: event.timeStamp,
|
||||
@ -1282,6 +1305,9 @@ sealed class BaseTapAndDragGestureRecognizer extends OneSequenceGestureRecognize
|
||||
}
|
||||
|
||||
void _checkDragEnd() {
|
||||
final Offset globalPosition = _currentPosition.global;
|
||||
final Offset localPosition = _currentPosition.local;
|
||||
|
||||
if (_dragUpdateThrottleTimer != null) {
|
||||
// If there's already an update scheduled, trigger it immediately and
|
||||
// cancel the timer.
|
||||
@ -1290,6 +1316,8 @@ sealed class BaseTapAndDragGestureRecognizer extends OneSequenceGestureRecognize
|
||||
}
|
||||
|
||||
final TapDragEndDetails endDetails = TapDragEndDetails(
|
||||
globalPosition: globalPosition,
|
||||
localPosition: localPosition,
|
||||
primaryVelocity: 0.0,
|
||||
consecutiveTapCount: consecutiveTapCount,
|
||||
);
|
||||
|
@ -1027,4 +1027,30 @@ void main() {
|
||||
GestureBinding.instance.gestureArena.sweep(1);
|
||||
expect(events, <String>['down#1']);
|
||||
});
|
||||
|
||||
testGesture('Contains correct positions in the drag end details', (GestureTester tester) {
|
||||
late TapDragEndDetails tapDragEndDetails;
|
||||
tapAndDrag =
|
||||
TapAndHorizontalDragGestureRecognizer()
|
||||
..dragStartBehavior = DragStartBehavior.down
|
||||
..eagerVictoryOnDrag = true
|
||||
..maxConsecutiveTap = 3
|
||||
..onDragEnd = (TapDragEndDetails details) {
|
||||
tapDragEndDetails = details;
|
||||
};
|
||||
addTearDown(tapAndDrag.dispose);
|
||||
|
||||
final TestPointer pointer = TestPointer(5);
|
||||
final PointerDownEvent pointerDown = pointer.down(const Offset(10.0, 10.0));
|
||||
|
||||
tapAndDrag.addPointer(pointerDown);
|
||||
tester.closeArena(pointer.pointer);
|
||||
tester.route(pointerDown);
|
||||
tester.route(pointer.move(const Offset(50.0, 20.0)));
|
||||
tester.route(pointer.move(const Offset(90.0, 30.0)));
|
||||
tester.route(pointer.move(const Offset(120.0, 45.0)));
|
||||
tester.route(pointer.up());
|
||||
|
||||
expect(tapDragEndDetails.globalPosition, const Offset(120.0, 45.0));
|
||||
});
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user