How can I perform a scale (pinch to zoom) multi finger gesture in Flutter's widget tests?

Viewed 1168

I am writing widget tests for a widget that handles actions when the user performs a scale/zooming gesture on it by instantiating a GestureDetector with the onScaleUpdate property callback. I know how to perform drag, taps and long presses in widget tests, but I cannot find a way to perform scale gestures in widget tests.

I have tried several approaches, such as performing simultaneous drags on opposite directions:

final myWidget = find.byKey(const Key("myWidget"));
await tester.drag(myWidget, Offset(100, 0));
await tester.drag(myWidget, Offset(-100, 0));

but the drags can't happen simultaneously, the framework forces me to await until a drag is finished before performing the second drag.

Is there any way to perform scaling / pinch-to-zoom / multi finger gestures in widget tests?

3 Answers

You can use createGesture() or startGesture() methods of WidgetTester to create two or more touches and control them. Here is an example:

    final widgetFinder = find.byKey(ValueKey('Scalable widget'));
    final center = tester.getCenter(widgetFinder);
    
    // create two touches:
    final touch1 = await tester.startGesture(center.translate(-10, 0));
    final touch2 = await tester.startGesture(center.translate(10, 0));

    // zoom in:
    await touch1.moveBy(Offset(-100, 0));
    await touch2.moveBy(Offset(100, 0));
    await tester.pump();
    
    // zoom out:
    await touch1.moveBy(Offset(10, 0));
    await touch2.moveBy(Offset(-10, 0));
    await tester.pump();
    
    // cancel touches:
    await touch1.cancel();
    await touch2.cancel();

This is how you can do it:

// Create the finder
final interactiveViewFinder = find.byType(InteractiveViewer);

// Get the center      
final center = tester.getCenter(interactiveViewFinder);

// Zoom in:
final Offset scaleStart1 = center;
final Offset scaleStart2 = Offset(center.dx + 10.0, center.dy);
final Offset scaleEnd1 = Offset(center.dx - 10.0, center.dy);
final Offset scaleEnd2 = Offset(center.dx + 10.0, center.dy);
final TestGesture gesture = await tester.createGesture();
final TestGesture gesture2 = await tester.createGesture();
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();

Source: Flutter's own unit tests for InteractiveViewer: https://github.com/flutter/flutter/blob/7d368dcf0c00b45fef5b02c5cccb8aa5306234ba/packages/flutter/test/widgets/interactive_viewer_test.dart#L49

you can use this package pinch_zoom: ^1.0.0

Related