Add no-op callbacks to platform view gesture recognizer when necessary (#61671)

After #31935, Some one sequence gesture recognizers requires at least one callback to be able to compete in the arena. This PR adds the a no-op callback in the gesture recognizer in the platform view when the gesture recognizer does not have any callbacks. This way, all the gesture recognizers in the platform view can compete in the arena.
This commit is contained in:
Chris Yang 2020-07-17 12:13:22 -07:00 committed by GitHub
parent 99ef90472a
commit 30e556ddc5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 207 additions and 12 deletions

View file

@ -401,7 +401,19 @@ class _UiKitViewGestureRecognizer extends OneSequenceGestureRecognizer {
team.captain = this;
_gestureRecognizers = gestureRecognizerFactories.map(
(Factory<OneSequenceGestureRecognizer> 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<OneSequenceGestureRecognizer> 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;

View file

@ -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<OneSequenceGestureRecognizer>>{
Factory<VerticalDragGestureRecognizer>(
() {
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<OneSequenceGestureRecognizer>>{
Factory<LongPressGestureRecognizer>(
() {
return LongPressGestureRecognizer();
},
),
},
layoutDirection: TextDirection.ltr,
),
),
),
),
);
await tester.longPressAt(const Offset(50.0, 50.0));
expect(longPressAccessedByParent, false);
expect(
viewsController.motionEvents[currentViewId + 1],
orderedEquals(<FakeAndroidMotionEvent>[
const FakeAndroidMotionEvent(
AndroidViewController.kActionDown, <int>[0], <Offset>[Offset(50.0, 50.0)]),
const FakeAndroidMotionEvent(
AndroidViewController.kActionUp, <int>[0], <Offset>[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<OneSequenceGestureRecognizer>>{
Factory<TapGestureRecognizer>(
() {
return TapGestureRecognizer();
},
),
},
layoutDirection: TextDirection.ltr,
),
),
),
),
);
await tester.tapAt(const Offset(50.0, 50.0));
expect(tapAccessedByParent, false);
expect(
viewsController.motionEvents[currentViewId + 1],
orderedEquals(<FakeAndroidMotionEvent>[
const FakeAndroidMotionEvent(
AndroidViewController.kActionDown, <int>[0], <Offset>[Offset(50.0, 50.0)]),
const FakeAndroidMotionEvent(
AndroidViewController.kActionUp, <int>[0], <Offset>[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<OneSequenceGestureRecognizer>>{
Factory<VerticalDragGestureRecognizer>(
() {
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<OneSequenceGestureRecognizer>>{
Factory<LongPressGestureRecognizer>(
() {
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<OneSequenceGestureRecognizer>>{
Factory<TapGestureRecognizer>(
() {
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<OneSequenceGestureRecognizer>>{
Factory<VerticalDragGestureRecognizer>(
() {
return VerticalDragGestureRecognizer()
..onStart = (_) {}; // Add callback to enable recognizer
return VerticalDragGestureRecognizer();
},
),
},