Unverified Commit 333c9618 authored by Chris Yang's avatar Chris Yang Committed by GitHub

Extract common PlatformView functionality: Gesture and PointerEvent (#37497)

parent 0cd0c660
......@@ -7,6 +7,7 @@ import 'dart:typed_data';
import 'dart:ui';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'message_codec.dart';
import 'system_channels.dart';
......@@ -725,4 +726,7 @@ abstract class PlatformViewController {
///
/// See also [PlatformViewRegistry] which is a helper for managing platform view ids.
int get viewId;
/// Dispatches the `event` to the platform view.
void dispatchPointerEvent(PointerEvent event);
}
......@@ -603,7 +603,11 @@ class PlatformViewSurface extends LeafRenderObjectWidget {
/// The [controller] must not be null.
const PlatformViewSurface({
@required this.controller,
}) : assert(controller != null);
@required this.hitTestBehavior,
@required this.gestureRecognizers,
}) : assert(controller != null),
assert(hitTestBehavior != null),
assert(gestureRecognizers != null);
/// The controller for the platform view integrated by this [PlatformViewSurface].
///
......@@ -611,14 +615,60 @@ class PlatformViewSurface extends LeafRenderObjectWidget {
/// [PlatformViewController.viewId] identifies the platform view whose contents are painted by this widget.
final PlatformViewController controller;
/// Which gestures should be forwarded to the PlatformView.
///
/// {@macro flutter.widgets.platformViews.gestureRecognizersDescHead}
///
/// For example, with the following setup vertical drags will not be dispatched to the platform view
/// as the vertical drag gesture is claimed by the parent [GestureDetector].
///
/// ```dart
/// GestureDetector(
/// onVerticalDragStart: (DragStartDetails details) {},
/// child: PlatformViewSurface(
/// ),
/// )
/// ```
///
/// To get the [PlatformViewSurface] to claim the vertical drag gestures we can pass a vertical drag
/// gesture recognizer factory in [gestureRecognizers] e.g:
///
/// ```dart
/// GestureDetector(
/// onVerticalDragStart: (DragStartDetails details) {},
/// child: SizedBox(
/// width: 200.0,
/// height: 100.0,
/// child: PlatformViewSurface(
/// gestureRecognizers: <Factory<OneSequenceGestureRecognizer>>[
/// new Factory<OneSequenceGestureRecognizer>(
/// () => new EagerGestureRecognizer(),
/// ),
/// ].toSet(),
/// ),
/// ),
/// )
/// ```
///
/// {@macro flutter.widgets.platformViews.gestureRecognizersDescFoot}
// We use OneSequenceGestureRecognizers as they support gesture arena teams.
// TODO(amirh): get a list of GestureRecognizers here.
// https://github.com/flutter/flutter/issues/20953
final Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers;
/// {@macro flutter.widgets.platformViews.hittestParam}
final PlatformViewHitTestBehavior hitTestBehavior;
@override
RenderObject createRenderObject(BuildContext context) {
return PlatformViewRenderBox(controller: controller);
return PlatformViewRenderBox(controller: controller, gestureRecognizers: gestureRecognizers, hitTestBehavior: hitTestBehavior);
}
@override
void updateRenderObject(BuildContext context, PlatformViewRenderBox renderObject) {
renderObject
..controller = controller;
..controller = controller
..hitTestBehavior = hitTestBehavior
..updateGestureRecognizers(gestureRecognizers);
}
}
......@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/material.dart';
......@@ -15,7 +17,16 @@ void main() {
PlatformViewRenderBox platformViewRenderBox;
setUp((){
fakePlatformViewController = FakePlatformViewController(0);
platformViewRenderBox = PlatformViewRenderBox(controller: fakePlatformViewController);
platformViewRenderBox = PlatformViewRenderBox(
controller: fakePlatformViewController,
hitTestBehavior: PlatformViewHitTestBehavior.opaque,
gestureRecognizers: <Factory<OneSequenceGestureRecognizer>>{
Factory<VerticalDragGestureRecognizer>(
() {
return VerticalDragGestureRecognizer();
},
),
},);
});
test('layout should size to max constraint', () {
......
......@@ -8,6 +8,7 @@ import 'package:collection/collection.dart';
import 'package:flutter/painting.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
/// Used in internal testing.
class FakePlatformViewController extends PlatformViewController {
......@@ -16,10 +17,22 @@ class FakePlatformViewController extends PlatformViewController {
_id = id;
}
/// Events that are dispatched;
List<PointerEvent> dispatchedPointerEvents = <PointerEvent>[];
int _id;
@override
int get viewId => _id;
@override
void dispatchPointerEvent(PointerEvent event) {
dispatchedPointerEvents.add(event);
}
void clearTestingVariables() {
dispatchedPointerEvents.clear();
}
}
class FakeAndroidPlatformViewsController {
......
......@@ -1676,12 +1676,261 @@ void main() {
});
testWidgets('PlatformViewSurface should create platform view layer', (WidgetTester tester) async {
final PlatformViewSurface surface = PlatformViewSurface(controller: controller);
final PlatformViewSurface surface = PlatformViewSurface(
controller: controller,
hitTestBehavior: PlatformViewHitTestBehavior.opaque,
gestureRecognizers: const <Factory<OneSequenceGestureRecognizer>>{},);
await tester.pumpWidget(surface);
final PlatformViewLayer layer = tester.layers.firstWhere((Layer layer){
return layer is PlatformViewLayer;
});
expect(layer, isNotNull);
});
testWidgets('PlatformViewSurface can lose gesture arenas', (WidgetTester tester) async {
bool verticalDragAcceptedByParent = false;
await tester.pumpWidget(
Align(
alignment: Alignment.topLeft,
child: Container(
margin: const EdgeInsets.all(10.0),
child: GestureDetector(
onVerticalDragStart: (DragStartDetails d) {
verticalDragAcceptedByParent = true;
},
child: SizedBox(
width: 200.0,
height: 100.0,
child: PlatformViewSurface(
controller: controller,
gestureRecognizers: const <Factory<OneSequenceGestureRecognizer>>{},
hitTestBehavior: PlatformViewHitTestBehavior.opaque),
),
),
),
),
);
final TestGesture gesture = await tester.startGesture(const Offset(50.0, 50.0));
await gesture.moveBy(const Offset(0.0, 100.0));
await gesture.up();
expect(verticalDragAcceptedByParent, true);
expect(
controller.dispatchedPointerEvents,
isEmpty,
);
});
testWidgets('PlatformViewSurface gesture recognizers dispatch events', (WidgetTester tester) async {
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: PlatformViewSurface(
controller: controller,
hitTestBehavior: PlatformViewHitTestBehavior.opaque,
gestureRecognizers: <Factory<OneSequenceGestureRecognizer>>{
Factory<VerticalDragGestureRecognizer>(
() {
return VerticalDragGestureRecognizer()
..onStart = (_) {}; // Add callback to enable recognizer
},
),
},
),
),
),
),
);
final TestGesture gesture = await tester.startGesture(const Offset(50.0, 50.0));
await gesture.moveBy(const Offset(0.0, 100.0));
await gesture.up();
expect(verticalDragAcceptedByParent, false);
expect(
controller.dispatchedPointerEvents.length,
3,
);
});
testWidgets(
'PlatformViewSurface can claim gesture after all pointers are up', (WidgetTester tester) async {
bool verticalDragAcceptedByParent = false;
// The long press recognizer rejects the gesture after the PlatformViewSurface gets the pointer up event.
// This test makes sure that the PlatformViewSurface can win the gesture after it got the pointer up event.
await tester.pumpWidget(
Align(
alignment: Alignment.topLeft,
child: GestureDetector(
onVerticalDragStart: (DragStartDetails d) {
verticalDragAcceptedByParent = true;
},
onLongPress: () { },
child: SizedBox(
width: 200.0,
height: 100.0,
child: PlatformViewSurface(
controller: controller,
hitTestBehavior: PlatformViewHitTestBehavior.opaque,
gestureRecognizers: const <Factory<OneSequenceGestureRecognizer>>{},
),
),
),
),
);
final TestGesture gesture = await tester.startGesture(const Offset(50.0, 50.0));
await gesture.up();
expect(verticalDragAcceptedByParent, false);
expect(
controller.dispatchedPointerEvents.length,
2,
);
});
testWidgets('PlatformViewSurface rebuilt during gesture', (WidgetTester tester) async {
await tester.pumpWidget(
Align(
alignment: Alignment.topLeft,
child: SizedBox(
width: 200.0,
height: 100.0,
child: PlatformViewSurface(
controller: controller,
hitTestBehavior: PlatformViewHitTestBehavior.opaque,
gestureRecognizers: const <Factory<OneSequenceGestureRecognizer>>{},
),
),
),
);
final TestGesture gesture = await tester.startGesture(const Offset(50.0, 50.0));
await gesture.moveBy(const Offset(0.0, 100.0));
await tester.pumpWidget(
Align(
alignment: Alignment.topLeft,
child: SizedBox(
width: 200.0,
height: 100.0,
child: PlatformViewSurface(
controller: controller,
hitTestBehavior: PlatformViewHitTestBehavior.opaque,
gestureRecognizers: const <Factory<OneSequenceGestureRecognizer>>{},
),
),
),
);
await gesture.up();
expect(
controller.dispatchedPointerEvents.length,
3,
);
});
testWidgets('PlatformViewSurface with eager gesture recognizer', (WidgetTester tester) async {
await tester.pumpWidget(
Align(
alignment: Alignment.topLeft,
child: GestureDetector(
onVerticalDragStart: (DragStartDetails d) { },
child: SizedBox(
width: 200.0,
height: 100.0,
child: PlatformViewSurface(
controller: controller,
hitTestBehavior: PlatformViewHitTestBehavior.opaque,
gestureRecognizers: <Factory<OneSequenceGestureRecognizer>>{
Factory<OneSequenceGestureRecognizer>(
() => EagerGestureRecognizer(),
),
},
),
),
),
),
);
await tester.startGesture(const Offset(50.0, 50.0));
// Normally (without the eager gesture recognizer) after just the pointer down event
// no gesture arena member will claim the arena (so no motion events will be dispatched to
// the PlatformViewSurface). Here we assert that with the eager recognizer in the gesture team the
// pointer down event is immediately dispatched.
expect(
controller.dispatchedPointerEvents.length,
1,
);
});
testWidgets('PlatformViewRenderBox reconstructed with same gestureRecognizers', (WidgetTester tester) async {
int factoryInvocationCount = 0;
final ValueGetter<EagerGestureRecognizer> constructRecognizer = () {
++ factoryInvocationCount;
return EagerGestureRecognizer();
};
final PlatformViewSurface platformViewSurface = PlatformViewSurface(
controller: controller,
hitTestBehavior: PlatformViewHitTestBehavior.opaque,
gestureRecognizers: <Factory<OneSequenceGestureRecognizer>>{
Factory<OneSequenceGestureRecognizer>(
constructRecognizer,
),
});
await tester.pumpWidget(platformViewSurface);
await tester.pumpWidget(const SizedBox.shrink());
await tester.pumpWidget(platformViewSurface);
expect(factoryInvocationCount, 2);
});
testWidgets('PlatformViewSurface rebuilt with same gestureRecognizers', (WidgetTester tester) async {
int factoryInvocationCount = 0;
final ValueGetter<EagerGestureRecognizer> constructRecognizer = () {
++ factoryInvocationCount;
return EagerGestureRecognizer();
};
await tester.pumpWidget(
PlatformViewSurface(
controller: controller,
hitTestBehavior: PlatformViewHitTestBehavior.opaque,
gestureRecognizers: <Factory<OneSequenceGestureRecognizer>>{
Factory<OneSequenceGestureRecognizer>(
constructRecognizer,
),
})
);
await tester.pumpWidget(
PlatformViewSurface(
controller: controller,
hitTestBehavior: PlatformViewHitTestBehavior.opaque,
gestureRecognizers: <Factory<OneSequenceGestureRecognizer>>{
Factory<OneSequenceGestureRecognizer>(
constructRecognizer,
),
})
);
expect(factoryInvocationCount, 1);
});
});
}
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