Unverified Commit fa71e802 authored by Henry Riehl's avatar Henry Riehl Committed by GitHub

Add position data to `OnDragEnd` callback (#140378)

This PR adds localPosition/globalPosition data to the `DragEndDetails`
argument of the `onDragEnd` callback.
parent ed02cf2f
...@@ -214,11 +214,14 @@ class DragEndDetails { ...@@ -214,11 +214,14 @@ class DragEndDetails {
DragEndDetails({ DragEndDetails({
this.velocity = Velocity.zero, this.velocity = Velocity.zero,
this.primaryVelocity, this.primaryVelocity,
this.globalPosition = Offset.zero,
Offset? localPosition,
}) : assert( }) : assert(
primaryVelocity == null primaryVelocity == null
|| (primaryVelocity == velocity.pixelsPerSecond.dx && velocity.pixelsPerSecond.dy == 0) || (primaryVelocity == velocity.pixelsPerSecond.dx && velocity.pixelsPerSecond.dy == 0)
|| (primaryVelocity == velocity.pixelsPerSecond.dy && velocity.pixelsPerSecond.dx == 0), || (primaryVelocity == velocity.pixelsPerSecond.dy && velocity.pixelsPerSecond.dx == 0),
); ),
localPosition = localPosition ?? globalPosition;
/// The velocity the pointer was moving when it stopped contacting the screen. /// The velocity the pointer was moving when it stopped contacting the screen.
/// ///
...@@ -237,6 +240,23 @@ class DragEndDetails { ...@@ -237,6 +240,23 @@ class DragEndDetails {
/// Defaults to null if not specified in the constructor. /// Defaults to null if not specified in the constructor.
final double? primaryVelocity; final double? primaryVelocity;
/// The global position the pointer is located at when the drag
/// gesture has been completed.
///
/// Defaults to the origin if not specified in the constructor.
///
/// 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 when
/// the drag gesture has been completed.
///
/// Defaults to [globalPosition] if not specified in the constructor.
final Offset localPosition;
@override @override
String toString() => '${objectRuntimeType(this, 'DragEndDetails')}($velocity)'; String toString() => '${objectRuntimeType(this, 'DragEndDetails')}($velocity)';
} }
...@@ -276,6 +276,7 @@ abstract class DragGestureRecognizer extends OneSequenceGestureRecognizer { ...@@ -276,6 +276,7 @@ abstract class DragGestureRecognizer extends OneSequenceGestureRecognizer {
_DragState _state = _DragState.ready; _DragState _state = _DragState.ready;
late OffsetPair _initialPosition; late OffsetPair _initialPosition;
late OffsetPair _pendingDragOffset; late OffsetPair _pendingDragOffset;
late OffsetPair _finalPosition;
Duration? _lastPendingEventTimestamp; Duration? _lastPendingEventTimestamp;
/// When asserts are enabled, returns the last tracked pending event timestamp /// When asserts are enabled, returns the last tracked pending event timestamp
...@@ -351,6 +352,7 @@ abstract class DragGestureRecognizer extends OneSequenceGestureRecognizer { ...@@ -351,6 +352,7 @@ abstract class DragGestureRecognizer extends OneSequenceGestureRecognizer {
if (_state == _DragState.ready) { if (_state == _DragState.ready) {
_state = _DragState.possible; _state = _DragState.possible;
_initialPosition = OffsetPair(global: event.position, local: event.localPosition); _initialPosition = OffsetPair(global: event.position, local: event.localPosition);
_finalPosition = _initialPosition;
_pendingDragOffset = OffsetPair.zero; _pendingDragOffset = OffsetPair.zero;
_globalDistanceMoved = 0.0; _globalDistanceMoved = 0.0;
_lastPendingEventTimestamp = event.timeStamp; _lastPendingEventTimestamp = event.timeStamp;
...@@ -418,6 +420,7 @@ abstract class DragGestureRecognizer extends OneSequenceGestureRecognizer { ...@@ -418,6 +420,7 @@ abstract class DragGestureRecognizer extends OneSequenceGestureRecognizer {
final Offset localDelta = (event is PointerMoveEvent) ? event.localDelta : (event as PointerPanZoomUpdateEvent).localPanDelta; final Offset localDelta = (event is PointerMoveEvent) ? event.localDelta : (event as PointerPanZoomUpdateEvent).localPanDelta;
final Offset position = (event is PointerMoveEvent) ? event.position : (event.position + (event as PointerPanZoomUpdateEvent).pan); final Offset position = (event is PointerMoveEvent) ? event.position : (event.position + (event as PointerPanZoomUpdateEvent).pan);
final Offset localPosition = (event is PointerMoveEvent) ? event.localPosition : (event.localPosition + (event as PointerPanZoomUpdateEvent).localPan); final Offset localPosition = (event is PointerMoveEvent) ? event.localPosition : (event.localPosition + (event as PointerPanZoomUpdateEvent).localPan);
_finalPosition = OffsetPair(local: localPosition, global: position);
if (_state == _DragState.accepted) { if (_state == _DragState.accepted) {
_checkUpdate( _checkUpdate(
sourceTimeStamp: event.timeStamp, sourceTimeStamp: event.timeStamp,
...@@ -600,7 +603,11 @@ abstract class DragGestureRecognizer extends OneSequenceGestureRecognizer { ...@@ -600,7 +603,11 @@ abstract class DragGestureRecognizer extends OneSequenceGestureRecognizer {
? () => '$estimate; fling at ${details!.velocity}.' ? () => '$estimate; fling at ${details!.velocity}.'
: () => '$estimate; judged to not be a fling.'; : () => '$estimate; judged to not be a fling.';
} }
details ??= DragEndDetails(primaryVelocity: 0.0); details ??= DragEndDetails(
primaryVelocity: 0.0,
globalPosition: _finalPosition.global,
localPosition: _finalPosition.local,
);
invokeCallback<void>('onEnd', () => onEnd!(details!), debugReport: debugReport); invokeCallback<void>('onEnd', () => onEnd!(details!), debugReport: debugReport);
} }
...@@ -660,6 +667,8 @@ class VerticalDragGestureRecognizer extends DragGestureRecognizer { ...@@ -660,6 +667,8 @@ class VerticalDragGestureRecognizer extends DragGestureRecognizer {
return DragEndDetails( return DragEndDetails(
velocity: Velocity(pixelsPerSecond: Offset(0, dy)), velocity: Velocity(pixelsPerSecond: Offset(0, dy)),
primaryVelocity: dy, primaryVelocity: dy,
globalPosition: _finalPosition.global,
localPosition: _finalPosition.local,
); );
} }
...@@ -715,6 +724,8 @@ class HorizontalDragGestureRecognizer extends DragGestureRecognizer { ...@@ -715,6 +724,8 @@ class HorizontalDragGestureRecognizer extends DragGestureRecognizer {
return DragEndDetails( return DragEndDetails(
velocity: Velocity(pixelsPerSecond: Offset(dx, 0)), velocity: Velocity(pixelsPerSecond: Offset(dx, 0)),
primaryVelocity: dx, primaryVelocity: dx,
globalPosition: _finalPosition.global,
localPosition: _finalPosition.local,
); );
} }
...@@ -765,7 +776,11 @@ class PanGestureRecognizer extends DragGestureRecognizer { ...@@ -765,7 +776,11 @@ class PanGestureRecognizer extends DragGestureRecognizer {
} }
final Velocity velocity = Velocity(pixelsPerSecond: estimate.pixelsPerSecond) final Velocity velocity = Velocity(pixelsPerSecond: estimate.pixelsPerSecond)
.clampMagnitude(minFlingVelocity ?? kMinFlingVelocity, maxFlingVelocity ?? kMaxFlingVelocity); .clampMagnitude(minFlingVelocity ?? kMinFlingVelocity, maxFlingVelocity ?? kMaxFlingVelocity);
return DragEndDetails(velocity: velocity); return DragEndDetails(
velocity: velocity,
globalPosition: _finalPosition.global,
localPosition: _finalPosition.local,
);
} }
@override @override
......
...@@ -125,6 +125,40 @@ void main() { ...@@ -125,6 +125,40 @@ void main() {
expect(didEndPan, isTrue); expect(didEndPan, isTrue);
}); });
testWidgets('DragEndDetails returns the last known position', (WidgetTester tester) async {
Offset updateOffset = const Offset(10.0, 10.0);
const EdgeInsets paddingOffset = EdgeInsets.all(10.0);
Offset? endOffset;
Offset? globalEndOffset;
await tester.pumpWidget(
Padding(
padding: paddingOffset,
child: GestureDetector(
onPanStart: (DragStartDetails details) {
},
onPanUpdate: (DragUpdateDetails details) {
updateOffset += details.delta;
},
onPanEnd: (DragEndDetails details) {
endOffset = details.localPosition;
globalEndOffset = details.globalPosition;
},
child: Container(
color: const Color(0xFF00FF00),
),
),
),
);
await tester.dragFrom(const Offset(20.0, 20.0), const Offset(30.0, 40.0));
expect(endOffset, isNotNull);
expect(updateOffset, endOffset);
// Make sure details.globalPosition works correctly.
expect(Offset(endOffset!.dx + paddingOffset.left, endOffset!.dy + paddingOffset.top), globalEndOffset);
});
group('Tap', () { group('Tap', () {
final ButtonVariant buttonVariant = ButtonVariant( final ButtonVariant buttonVariant = ButtonVariant(
values: <int>[kPrimaryButton, kSecondaryButton, kTertiaryButton], values: <int>[kPrimaryButton, kSecondaryButton, kTertiaryButton],
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment