InteractiveViewer Scale Jump (#71497)
This commit is contained in:
parent
df748f1a89
commit
4e3b01152e
@ -2,7 +2,6 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
|
||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
|
|
||||||
import 'package:vector_math/vector_math_64.dart';
|
import 'package:vector_math/vector_math_64.dart';
|
||||||
@ -226,10 +225,42 @@ class ScaleGestureRecognizer extends OneSequenceGestureRecognizer {
|
|||||||
ScaleGestureRecognizer({
|
ScaleGestureRecognizer({
|
||||||
Object? debugOwner,
|
Object? debugOwner,
|
||||||
PointerDeviceKind? kind,
|
PointerDeviceKind? kind,
|
||||||
}) : super(debugOwner: debugOwner, kind: kind);
|
this.dragStartBehavior = DragStartBehavior.down,
|
||||||
|
}) : assert(dragStartBehavior != null),
|
||||||
|
super(debugOwner: debugOwner, kind: kind);
|
||||||
|
|
||||||
|
/// Determines what point is used as the starting point in all calculations
|
||||||
|
/// involving this gesture.
|
||||||
|
///
|
||||||
|
/// When set to [DragStartBehavior.down], the scale is calculated starting
|
||||||
|
/// from the position where the pointer first contacted the screen.
|
||||||
|
///
|
||||||
|
/// When set to [DragStartBehavior.start], the scale is calculated starting
|
||||||
|
/// from the position where the scale gesture began. The scale gesture may
|
||||||
|
/// begin after the time that the pointer first contacted the screen if there
|
||||||
|
/// are multiple listeners competing for the gesture. In that case, the
|
||||||
|
/// gesture arena waits to determine whether or not the gesture is a scale
|
||||||
|
/// gesture before giving the gesture to this GestureRecognizer. This happens
|
||||||
|
/// in the case of nested GestureDetectors, for example.
|
||||||
|
///
|
||||||
|
/// Defaults to [DragStartBehavior.down].
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [https://flutter.dev/docs/development/ui/advanced/gestures#gesture-disambiguation],
|
||||||
|
/// which provides more information about the gesture arena.
|
||||||
|
DragStartBehavior dragStartBehavior;
|
||||||
|
|
||||||
/// The pointers in contact with the screen have established a focal point and
|
/// The pointers in contact with the screen have established a focal point and
|
||||||
/// initial scale of 1.0.
|
/// initial scale of 1.0.
|
||||||
|
///
|
||||||
|
/// This won't be called until the gesture arena has determined that this
|
||||||
|
/// GestureRecognizer has won the gesture.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [https://flutter.dev/docs/development/ui/advanced/gestures#gesture-disambiguation],
|
||||||
|
/// which provides more information about the gesture arena.
|
||||||
GestureScaleStartCallback? onStart;
|
GestureScaleStartCallback? onStart;
|
||||||
|
|
||||||
/// The pointers in contact with the screen have indicated a new focal point
|
/// The pointers in contact with the screen have indicated a new focal point
|
||||||
@ -461,6 +492,13 @@ class ScaleGestureRecognizer extends OneSequenceGestureRecognizer {
|
|||||||
if (_state == _ScaleState.possible) {
|
if (_state == _ScaleState.possible) {
|
||||||
_state = _ScaleState.started;
|
_state = _ScaleState.started;
|
||||||
_dispatchOnStartCallbackIfNeeded();
|
_dispatchOnStartCallbackIfNeeded();
|
||||||
|
if (dragStartBehavior == DragStartBehavior.start) {
|
||||||
|
_initialFocalPoint = _currentFocalPoint;
|
||||||
|
_initialSpan = _currentSpan;
|
||||||
|
_initialLine = _currentLine;
|
||||||
|
_initialHorizontalSpan = _currentHorizontalSpan;
|
||||||
|
_initialVerticalSpan = _currentVerticalSpan;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -897,7 +897,8 @@ class GestureDetector extends StatelessWidget {
|
|||||||
instance
|
instance
|
||||||
..onStart = onScaleStart
|
..onStart = onScaleStart
|
||||||
..onUpdate = onScaleUpdate
|
..onUpdate = onScaleUpdate
|
||||||
..onEnd = onScaleEnd;
|
..onEnd = onScaleEnd
|
||||||
|
..dragStartBehavior = dragStartBehavior;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1078,6 +1078,7 @@ class _InteractiveViewerState extends State<InteractiveViewer> with TickerProvid
|
|||||||
onPointerSignal: _receivedPointerSignal,
|
onPointerSignal: _receivedPointerSignal,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
behavior: HitTestBehavior.opaque, // Necessary when panning off screen.
|
behavior: HitTestBehavior.opaque, // Necessary when panning off screen.
|
||||||
|
dragStartBehavior: DragStartBehavior.start,
|
||||||
onScaleEnd: _onScaleEnd,
|
onScaleEnd: _onScaleEnd,
|
||||||
onScaleStart: _onScaleStart,
|
onScaleStart: _onScaleStart,
|
||||||
onScaleUpdate: _onScaleUpdate,
|
onScaleUpdate: _onScaleUpdate,
|
||||||
|
@ -907,6 +907,84 @@ void main() {
|
|||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
expect(transformationController.value, equals(Matrix4.identity()));
|
expect(transformationController.value, equals(Matrix4.identity()));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('scale does not jump when wrapped in GestureDetector', (WidgetTester tester) async {
|
||||||
|
final TransformationController transformationController = TransformationController();
|
||||||
|
double? initialScale;
|
||||||
|
double? scale;
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: Scaffold(
|
||||||
|
body: Center(
|
||||||
|
child: GestureDetector(
|
||||||
|
onTapUp: (TapUpDetails details) {},
|
||||||
|
child: InteractiveViewer(
|
||||||
|
onInteractionUpdate: (ScaleUpdateDetails details) {
|
||||||
|
initialScale ??= details.scale;
|
||||||
|
scale = details.scale;
|
||||||
|
},
|
||||||
|
transformationController: transformationController,
|
||||||
|
child: Container(width: 200.0, height: 200.0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(transformationController.value, equals(Matrix4.identity()));
|
||||||
|
expect(initialScale, null);
|
||||||
|
expect(scale, null);
|
||||||
|
|
||||||
|
// Pinch to zoom isn't immediately detected for a small amount of
|
||||||
|
// movement due to the GestureDetector.
|
||||||
|
final Offset childOffset = tester.getTopLeft(find.byType(Container));
|
||||||
|
final Offset childInterior = Offset(
|
||||||
|
childOffset.dx + 20.0,
|
||||||
|
childOffset.dy + 20.0,
|
||||||
|
);
|
||||||
|
final Offset scaleStart1 = childInterior;
|
||||||
|
final Offset scaleStart2 = Offset(childInterior.dx + 10.0, childInterior.dy);
|
||||||
|
Offset scaleEnd1 = Offset(childInterior.dx - 10.0, childInterior.dy);
|
||||||
|
Offset scaleEnd2 = Offset(childInterior.dx + 20.0, childInterior.dy);
|
||||||
|
TestGesture gesture = await tester.createGesture();
|
||||||
|
TestGesture gesture2 = await tester.createGesture();
|
||||||
|
addTearDown(gesture.removePointer);
|
||||||
|
addTearDown(gesture2.removePointer);
|
||||||
|
await gesture.down(scaleStart1);
|
||||||
|
await gesture2.down(scaleStart2);
|
||||||
|
await tester.pump();
|
||||||
|
await gesture.moveTo(scaleEnd1);
|
||||||
|
await gesture2.moveTo(scaleEnd2);
|
||||||
|
await tester.pump();
|
||||||
|
await gesture.up();
|
||||||
|
await gesture2.up();
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(transformationController.value, equals(Matrix4.identity()));
|
||||||
|
expect(initialScale, null);
|
||||||
|
expect(scale, null);
|
||||||
|
|
||||||
|
// Pinch to zoom for a larger amount is detected. It starts smoothly at
|
||||||
|
// 1.0 despite the fact that the gesture has already moved a bit.
|
||||||
|
scaleEnd1 = Offset(childInterior.dx - 38.0, childInterior.dy);
|
||||||
|
scaleEnd2 = Offset(childInterior.dx + 48.0, childInterior.dy);
|
||||||
|
gesture = await tester.createGesture();
|
||||||
|
gesture2 = await tester.createGesture();
|
||||||
|
addTearDown(gesture.removePointer);
|
||||||
|
addTearDown(gesture2.removePointer);
|
||||||
|
await gesture.down(scaleStart1);
|
||||||
|
await gesture2.down(scaleStart2);
|
||||||
|
await tester.pump();
|
||||||
|
await gesture.moveTo(scaleEnd1);
|
||||||
|
await gesture2.moveTo(scaleEnd2);
|
||||||
|
await tester.pump();
|
||||||
|
await gesture.up();
|
||||||
|
await gesture2.up();
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(initialScale, 1.0);
|
||||||
|
expect(scale, greaterThan(1.0));
|
||||||
|
expect(transformationController.value.getMaxScaleOnAxis(), greaterThan(1.0));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
group('getNearestPointOnLine', () {
|
group('getNearestPointOnLine', () {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user