Unverified Commit 5099701f authored by Amir Hardon's avatar Amir Hardon Committed by GitHub

Make RenderUiKitView reject absorbed touch events (#28666)

When a touch event that is in the bounds of a RenderUiKitView is absorbed by another render object,
the RenderUiKitView's handleEvent is not called for that object. On the platform side, the touch event hits the FlutterTouchInterceptingView which is waiting for a framework decision that never arrived on whether to reject or accept the gesture.

This change fixes the issue by having RenderUiKitView register a global PointerRoute, that is used to reject absorbed touch events.
parent 013fd211
......@@ -323,6 +323,8 @@ class RenderUiKitView extends RenderBox {
_UiKitViewGestureRecognizer _gestureRecognizer;
PointerEvent _lastPointerDownEvent;
@override
void performResize() {
size = constraints.biggest;
......@@ -349,13 +351,41 @@ class RenderUiKitView extends RenderBox {
@override
void handleEvent(PointerEvent event, HitTestEntry entry) {
if (event is PointerDownEvent) {
if (event is! PointerDownEvent) {
return;
}
_gestureRecognizer.addPointer(event);
_lastPointerDownEvent = event;
}
// This is registered as a global PointerRoute while the render object is attached.
void _handleGlobalPointerEvent(PointerEvent event) {
if (event is! PointerDownEvent) {
return;
}
final Offset localOffset = globalToLocal(event.position);
if(!(Offset.zero & size).contains(localOffset)) {
return;
}
if (event != _lastPointerDownEvent) {
// The pointer event is in the bounds of this render box, but we didn't get it in handleEvent.
// This means that the pointer event was absorbed by a different render object.
// Since on the platform side the FlutterTouchIntercepting view is seeing all events that are
// within its bounds we need to tell it to reject the current touch sequence.
_viewController.rejectGesture();
}
_lastPointerDownEvent = null;
}
@override
void attach(PipelineOwner owner) {
super.attach(owner);
GestureBinding.instance.pointerRouter.addGlobalRoute(_handleGlobalPointerEvent);
}
@override
void detach() {
GestureBinding.instance.pointerRouter.removeGlobalRoute(_handleGlobalPointerEvent);
_gestureRecognizer.reset();
super.detach();
}
......
......@@ -1364,6 +1364,36 @@ void main() {
expect(viewsController.gesturesRejected[currentViewId + 1], 0);
});
testWidgets('UiKitView rejects gestures absorbed by siblings', (WidgetTester tester) async {
final int currentViewId = platformViewsRegistry.getNextPlatformViewId();
final FakeIosPlatformViewsController viewsController = FakeIosPlatformViewsController();
viewsController.registerViewType('webview');
await tester.pumpWidget(
Stack(
alignment: Alignment.topLeft,
children: <Widget>[
const UiKitView(viewType: 'webview', layoutDirection: TextDirection.ltr),
Container(
color: const Color.fromARGB(255, 255, 255, 255),
width: 100,
height: 100,
),
],
)
);
// First frame is before the platform view was created so the render object
// is not yet in the tree.
await tester.pump();
final TestGesture gesture = await tester.startGesture(const Offset(50.0, 50.0));
await gesture.up();
expect(viewsController.gesturesRejected[currentViewId + 1], 1);
expect(viewsController.gesturesAccepted[currentViewId + 1], 0);
});
testWidgets('AndroidView rebuilt with same gestureRecognizers', (WidgetTester tester) async {
final FakeIosPlatformViewsController viewsController = FakeIosPlatformViewsController();
viewsController.registerViewType('webview');
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment