Skip to content

Commit 32dc1b7

Browse files
authored
Add [pointerCount] property to Scale Gesture Details (flutter#73474)
1 parent 80f15dc commit 32dc1b7

File tree

4 files changed

+89
-9
lines changed

4 files changed

+89
-9
lines changed

packages/flutter/lib/src/gestures/scale.dart

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ class ScaleStartDetails {
3636
/// Creates details for [GestureScaleStartCallback].
3737
///
3838
/// The [focalPoint] argument must not be null.
39-
ScaleStartDetails({ this.focalPoint = Offset.zero, Offset? localFocalPoint, })
39+
ScaleStartDetails({ this.focalPoint = Offset.zero, Offset? localFocalPoint, this.pointerCount = 0 })
4040
: assert(focalPoint != null), localFocalPoint = localFocalPoint ?? focalPoint;
4141

4242
/// The initial focal point of the pointers in contact with the screen.
@@ -60,8 +60,14 @@ class ScaleStartDetails {
6060
/// coordinates.
6161
final Offset localFocalPoint;
6262

63+
/// The number of pointers being tracked by the gesture recognizer.
64+
///
65+
/// Typically this is the number of fingers being used to pan the widget using the gesture
66+
/// recognizer.
67+
final int pointerCount;
68+
6369
@override
64-
String toString() => 'ScaleStartDetails(focalPoint: $focalPoint, localFocalPoint: $localFocalPoint)';
70+
String toString() => 'ScaleStartDetails(focalPoint: $focalPoint, localFocalPoint: $localFocalPoint, pointersCount: $pointerCount)';
6571
}
6672

6773
/// Details for [GestureScaleUpdateCallback].
@@ -78,6 +84,7 @@ class ScaleUpdateDetails {
7884
this.horizontalScale = 1.0,
7985
this.verticalScale = 1.0,
8086
this.rotation = 0.0,
87+
this.pointerCount = 0,
8188
}) : assert(focalPoint != null),
8289
assert(scale != null && scale >= 0.0),
8390
assert(horizontalScale != null && horizontalScale >= 0.0),
@@ -145,23 +152,42 @@ class ScaleUpdateDetails {
145152
/// Expressed in radians.
146153
final double rotation;
147154

155+
/// The number of pointers being tracked by the gesture recognizer.
156+
///
157+
/// Typically this is the number of fingers being used to pan the widget using the gesture
158+
/// recognizer.
159+
final int pointerCount;
160+
148161
@override
149-
String toString() => 'ScaleUpdateDetails(focalPoint: $focalPoint, localFocalPoint: $localFocalPoint, scale: $scale, horizontalScale: $horizontalScale, verticalScale: $verticalScale, rotation: $rotation)';
162+
String toString() => 'ScaleUpdateDetails('
163+
'focalPoint: $focalPoint,'
164+
' localFocalPoint: $localFocalPoint,'
165+
' scale: $scale,'
166+
' horizontalScale: $horizontalScale,'
167+
' verticalScale: $verticalScale,'
168+
' rotation: $rotation,'
169+
' pointerCount: $pointerCount)';
150170
}
151171

152172
/// Details for [GestureScaleEndCallback].
153173
class ScaleEndDetails {
154174
/// Creates details for [GestureScaleEndCallback].
155175
///
156176
/// The [velocity] argument must not be null.
157-
ScaleEndDetails({ this.velocity = Velocity.zero })
177+
ScaleEndDetails({ this.velocity = Velocity.zero, this.pointerCount = 0 })
158178
: assert(velocity != null);
159179

160180
/// The velocity of the last pointer to be lifted off of the screen.
161181
final Velocity velocity;
162182

183+
/// The number of pointers being tracked by the gesture recognizer.
184+
///
185+
/// Typically this is the number of fingers being used to pan the widget using the gesture
186+
/// recognizer.
187+
final int pointerCount;
188+
163189
@override
164-
String toString() => 'ScaleEndDetails(velocity: $velocity)';
190+
String toString() => 'ScaleEndDetails(velocity: $velocity, pointerCount: $pointerCount)';
165191
}
166192

167193
/// Signature for when the pointers in contact with the screen have established
@@ -434,9 +460,9 @@ class ScaleGestureRecognizer extends OneSequenceGestureRecognizer {
434460
final Offset pixelsPerSecond = velocity.pixelsPerSecond;
435461
if (pixelsPerSecond.distanceSquared > kMaxFlingVelocity * kMaxFlingVelocity)
436462
velocity = Velocity(pixelsPerSecond: (pixelsPerSecond / pixelsPerSecond.distance) * kMaxFlingVelocity);
437-
invokeCallback<void>('onEnd', () => onEnd!(ScaleEndDetails(velocity: velocity)));
463+
invokeCallback<void>('onEnd', () => onEnd!(ScaleEndDetails(velocity: velocity, pointerCount: _pointerQueue.length)));
438464
} else {
439-
invokeCallback<void>('onEnd', () => onEnd!(ScaleEndDetails(velocity: Velocity.zero)));
465+
invokeCallback<void>('onEnd', () => onEnd!(ScaleEndDetails(velocity: Velocity.zero, pointerCount: _pointerQueue.length)));
440466
}
441467
}
442468
_state = _ScaleState.accepted;
@@ -472,6 +498,7 @@ class ScaleGestureRecognizer extends OneSequenceGestureRecognizer {
472498
focalPoint: _currentFocalPoint,
473499
localFocalPoint: PointerEvent.transformPosition(_lastTransform, _currentFocalPoint),
474500
rotation: _computeRotationFactor(),
501+
pointerCount: _pointerQueue.length,
475502
));
476503
});
477504
}
@@ -483,6 +510,7 @@ class ScaleGestureRecognizer extends OneSequenceGestureRecognizer {
483510
onStart!(ScaleStartDetails(
484511
focalPoint: _currentFocalPoint,
485512
localFocalPoint: PointerEvent.transformPosition(_lastTransform, _currentFocalPoint),
513+
pointerCount: _pointerQueue.length,
486514
));
487515
});
488516
}

packages/flutter/test/foundation/bit_field_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import 'package:flutter/foundation.dart';
66
import '../flutter_test_alternative.dart';
77

88
// ignore: unused_field
9-
enum _TestEnum { a, b, c, d, e, f, g, h, }
9+
enum _TestEnum { a, b, c, d, e, f, g, h }
1010

1111
void main() {
1212
test('BitField control test', () {

packages/flutter/test/gestures/scale_test.dart

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,4 +571,56 @@ void main() {
571571
scale.dispose();
572572
tap.dispose();
573573
});
574+
575+
testGesture('Scale gestures pointer count test', (GestureTester tester) {
576+
final ScaleGestureRecognizer scale = ScaleGestureRecognizer();
577+
578+
int pointerCountOfStart = 0;
579+
scale.onStart = (ScaleStartDetails details) => pointerCountOfStart = details.pointerCount;
580+
581+
int pointerCountOfUpdate = 0;
582+
scale.onUpdate = (ScaleUpdateDetails details) => pointerCountOfUpdate = details.pointerCount;
583+
584+
int pointerCountOfEnd = 0;
585+
scale.onEnd = (ScaleEndDetails details) => pointerCountOfEnd = details.pointerCount;
586+
587+
final TestPointer pointer1 = TestPointer(1);
588+
final PointerDownEvent down = pointer1.down(const Offset(0.0, 0.0));
589+
scale.addPointer(down);
590+
tester.closeArena(1);
591+
592+
// One-finger panning
593+
tester.route(down);
594+
// One pointer in contact with the screen now.
595+
expect(pointerCountOfStart, 1);
596+
tester.route(pointer1.move(const Offset(20.0, 30.0)));
597+
expect(pointerCountOfUpdate, 1);
598+
599+
// Two-finger scaling
600+
final TestPointer pointer2 = TestPointer(2);
601+
final PointerDownEvent down2 = pointer2.down(const Offset(10.0, 20.0));
602+
scale.addPointer(down2);
603+
tester.closeArena(2);
604+
tester.route(down2);
605+
// Two pointers in contact with the screen now.
606+
expect(pointerCountOfEnd, 2); // Additional pointer down will trigger an end event.
607+
608+
tester.route(pointer2.move(const Offset(0.0, 10.0)));
609+
expect(pointerCountOfStart, 2); // The new pointer move will trigger a start event.
610+
expect(pointerCountOfUpdate, 2);
611+
612+
tester.route(pointer1.up());
613+
// One pointer in contact with the screen now.
614+
expect(pointerCountOfEnd, 1);
615+
616+
tester.route(pointer2.move(const Offset(0.0, 10.0)));
617+
expect(pointerCountOfStart, 1);
618+
expect(pointerCountOfUpdate, 1);
619+
620+
tester.route(pointer2.up());
621+
// No pointer in contact with the screen now.
622+
expect(pointerCountOfEnd, 0);
623+
624+
scale.dispose();
625+
});
574626
}

packages/flutter/test/material/text_field_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8695,7 +8695,7 @@ void main() {
86958695
// Regressing test for https://github.com/flutter/flutter/issues/70625
86968696
testWidgets('TextFields can inherit [FloatingLabelBehaviour] from InputDecorationTheme.', (WidgetTester tester) async {
86978697
final FocusNode focusNode = FocusNode();
8698-
Widget textFieldBuilder({FloatingLabelBehavior behavior = FloatingLabelBehavior.auto, }) {
8698+
Widget textFieldBuilder({ FloatingLabelBehavior behavior = FloatingLabelBehavior.auto }) {
86998699
return MaterialApp(
87008700
theme: ThemeData(
87018701
inputDecorationTheme: InputDecorationTheme(

0 commit comments

Comments
 (0)