diff --git a/packages/flutter/lib/src/rendering/platform_view.dart b/packages/flutter/lib/src/rendering/platform_view.dart index 6d85d493aa..e34c31125f 100644 --- a/packages/flutter/lib/src/rendering/platform_view.dart +++ b/packages/flutter/lib/src/rendering/platform_view.dart @@ -401,7 +401,19 @@ class _UiKitViewGestureRecognizer extends OneSequenceGestureRecognizer { team.captain = this; _gestureRecognizers = gestureRecognizerFactories.map( (Factory recognizerFactory) { - return recognizerFactory.constructor()..team = team; + final OneSequenceGestureRecognizer gestureRecognizer = recognizerFactory.constructor(); + gestureRecognizer.team = team; + // The below gesture recognizers requires at least one non-empty callback to + // compete in the gesture arena. + // https://github.com/flutter/flutter/issues/35394#issuecomment-562285087 + if (gestureRecognizer is LongPressGestureRecognizer) { + gestureRecognizer.onLongPress ??= (){}; + } else if (gestureRecognizer is DragGestureRecognizer) { + gestureRecognizer.onDown ??= (_){}; + } else if (gestureRecognizer is TapGestureRecognizer) { + gestureRecognizer.onTapDown ??= (_){}; + } + return gestureRecognizer; }, ).toSet(); } @@ -467,7 +479,19 @@ class _PlatformViewGestureRecognizer extends OneSequenceGestureRecognizer { team.captain = this; _gestureRecognizers = gestureRecognizerFactories.map( (Factory recognizerFactory) { - return recognizerFactory.constructor()..team = team; + final OneSequenceGestureRecognizer gestureRecognizer = recognizerFactory.constructor(); + gestureRecognizer.team = team; + // The below gesture recognizers requires at least one non-empty callback to + // compete in the gesture arena. + // https://github.com/flutter/flutter/issues/35394#issuecomment-562285087 + if (gestureRecognizer is LongPressGestureRecognizer) { + gestureRecognizer.onLongPress ??= (){}; + } else if (gestureRecognizer is DragGestureRecognizer) { + gestureRecognizer.onDown ??= (_){}; + } else if (gestureRecognizer is TapGestureRecognizer) { + gestureRecognizer.onTapDown ??= (_){}; + } + return gestureRecognizer; }, ).toSet(); _handlePointerEvent = handlePointerEvent; diff --git a/packages/flutter/test/widgets/platform_view_test.dart b/packages/flutter/test/widgets/platform_view_test.dart index 4e95396ebf..d4a5661319 100644 --- a/packages/flutter/test/widgets/platform_view_test.dart +++ b/packages/flutter/test/widgets/platform_view_test.dart @@ -576,7 +576,7 @@ void main() { ); }); - testWidgets('Android view gesture recognizers', (WidgetTester tester) async { + testWidgets('Android view drag gesture recognizer', (WidgetTester tester) async { final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); final FakeAndroidPlatformViewsController viewsController = FakeAndroidPlatformViewsController(); viewsController.registerViewType('webview'); @@ -596,8 +596,7 @@ void main() { gestureRecognizers: >{ Factory( () { - return VerticalDragGestureRecognizer() - ..onStart = (_) {}; // Add callback to enable recognizer + return VerticalDragGestureRecognizer(); }, ), }, @@ -626,6 +625,96 @@ void main() { ); }); + testWidgets('Android view long press gesture recognizer', (WidgetTester tester) async { + final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); + final FakeAndroidPlatformViewsController viewsController = FakeAndroidPlatformViewsController(); + viewsController.registerViewType('webview'); + bool longPressAccessedByParent = false; + await tester.pumpWidget( + Align( + alignment: Alignment.topLeft, + child: GestureDetector( + onLongPress: () { + longPressAccessedByParent = true; + }, + child: SizedBox( + width: 200.0, + height: 100.0, + child: AndroidView( + viewType: 'webview', + gestureRecognizers: >{ + Factory( + () { + return LongPressGestureRecognizer(); + }, + ), + }, + layoutDirection: TextDirection.ltr, + ), + ), + ), + ), + ); + + await tester.longPressAt(const Offset(50.0, 50.0)); + + expect(longPressAccessedByParent, false); + expect( + viewsController.motionEvents[currentViewId + 1], + orderedEquals([ + const FakeAndroidMotionEvent( + AndroidViewController.kActionDown, [0], [Offset(50.0, 50.0)]), + const FakeAndroidMotionEvent( + AndroidViewController.kActionUp, [0], [Offset(50.0, 50.0)]), + ]), + ); + }); + + testWidgets('Android view tap gesture recognizer', (WidgetTester tester) async { + final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); + final FakeAndroidPlatformViewsController viewsController = FakeAndroidPlatformViewsController(); + viewsController.registerViewType('webview'); + bool tapAccessedByParent = false; + await tester.pumpWidget( + Align( + alignment: Alignment.topLeft, + child: GestureDetector( + onTap: () { + tapAccessedByParent = true; + }, + child: SizedBox( + width: 200.0, + height: 100.0, + child: AndroidView( + viewType: 'webview', + gestureRecognizers: >{ + Factory( + () { + return TapGestureRecognizer(); + }, + ), + }, + layoutDirection: TextDirection.ltr, + ), + ), + ), + ), + ); + + await tester.tapAt(const Offset(50.0, 50.0)); + + expect(tapAccessedByParent, false); + expect( + viewsController.motionEvents[currentViewId + 1], + orderedEquals([ + const FakeAndroidMotionEvent( + AndroidViewController.kActionDown, [0], [Offset(50.0, 50.0)]), + const FakeAndroidMotionEvent( + AndroidViewController.kActionUp, [0], [Offset(50.0, 50.0)]), + ]), + ); + }); + testWidgets('Android view can claim gesture after all pointers are up', (WidgetTester tester) async { final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); final FakeAndroidPlatformViewsController viewsController = FakeAndroidPlatformViewsController(); @@ -1422,17 +1511,17 @@ void main() { expect(viewsController.gesturesRejected[currentViewId + 1], 1); }); - testWidgets('UiKitView gesture recognizers', (WidgetTester tester) async { + testWidgets('UiKitView tap gesture recognizers', (WidgetTester tester) async { final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); final FakeIosPlatformViewsController viewsController = FakeIosPlatformViewsController(); viewsController.registerViewType('webview'); - bool verticalDragAcceptedByParent = false; + bool gestureAcceptedByParent = false; await tester.pumpWidget( Align( alignment: Alignment.topLeft, child: GestureDetector( onVerticalDragStart: (DragStartDetails d) { - verticalDragAcceptedByParent = true; + gestureAcceptedByParent = true; }, child: SizedBox( width: 200.0, @@ -1442,8 +1531,7 @@ void main() { gestureRecognizers: >{ Factory( () { - return VerticalDragGestureRecognizer() - ..onStart = (_) {}; // Add callback to enable recognizer + return VerticalDragGestureRecognizer(); }, ), }, @@ -1462,6 +1550,90 @@ void main() { await gesture.moveBy(const Offset(0.0, 100.0)); await gesture.up(); + expect(gestureAcceptedByParent, false); + expect(viewsController.gesturesAccepted[currentViewId + 1], 1); + expect(viewsController.gesturesRejected[currentViewId + 1], 0); + }); + + testWidgets('UiKitView long press gesture recognizers', (WidgetTester tester) async { + final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); + final FakeIosPlatformViewsController viewsController = FakeIosPlatformViewsController(); + viewsController.registerViewType('webview'); + bool gestureAcceptedByParent = false; + await tester.pumpWidget( + Align( + alignment: Alignment.topLeft, + child: GestureDetector( + onLongPress: () { + gestureAcceptedByParent = true; + }, + child: SizedBox( + width: 200.0, + height: 100.0, + child: UiKitView( + viewType: 'webview', + gestureRecognizers: >{ + Factory( + () { + return LongPressGestureRecognizer(); + }, + ), + }, + layoutDirection: TextDirection.ltr, + ), + ), + ), + ), + ); + + // First frame is before the platform view was created so the render object + // is not yet in the tree. + await tester.pump(); + + await tester.longPressAt(const Offset(50.0, 50.0)); + + expect(gestureAcceptedByParent, false); + expect(viewsController.gesturesAccepted[currentViewId + 1], 1); + expect(viewsController.gesturesRejected[currentViewId + 1], 0); + }); + + testWidgets('UiKitView drag gesture recognizers', (WidgetTester tester) async { + final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); + final FakeIosPlatformViewsController viewsController = FakeIosPlatformViewsController(); + viewsController.registerViewType('webview'); + bool verticalDragAcceptedByParent = false; + await tester.pumpWidget( + Align( + alignment: Alignment.topLeft, + child: GestureDetector( + onVerticalDragStart: (DragStartDetails d) { + verticalDragAcceptedByParent = true; + }, + child: SizedBox( + width: 200.0, + height: 100.0, + child: UiKitView( + viewType: 'webview', + gestureRecognizers: >{ + Factory( + () { + return TapGestureRecognizer(); + }, + ), + }, + layoutDirection: TextDirection.ltr, + ), + ), + ), + ), + ); + + // First frame is before the platform view was created so the render object + // is not yet in the tree. + await tester.pump(); + + await tester.tapAt(const Offset(50.0, 50.0)); + expect(verticalDragAcceptedByParent, false); expect(viewsController.gesturesAccepted[currentViewId + 1], 1); expect(viewsController.gesturesRejected[currentViewId + 1], 0); @@ -1806,8 +1978,7 @@ void main() { gestureRecognizers: >{ Factory( () { - return VerticalDragGestureRecognizer() - ..onStart = (_) {}; // Add callback to enable recognizer + return VerticalDragGestureRecognizer(); }, ), },