Unverified Commit dc4bf652 authored by Amir Hardon's avatar Amir Hardon Committed by GitHub

Make UiKitViews participate in gesture arenas (#24027)

parent 0b953f92
...@@ -617,6 +617,13 @@ class UiKitViewController { ...@@ -617,6 +617,13 @@ class UiKitViewController {
// TODO(amirh): invoke the iOS platform views channel direction method once available. // TODO(amirh): invoke the iOS platform views channel direction method once available.
} }
Future<void> acceptGesture() {
final Map<String, dynamic> args = <String, dynamic> {
'id': id,
};
return SystemChannels.platform_views.invokeMethod('acceptGesture', args);
}
/// Disposes the view. /// Disposes the view.
/// ///
/// The [UiKitViewController] object is unusable after calling this. /// The [UiKitViewController] object is unusable after calling this.
......
...@@ -26,11 +26,13 @@ import 'framework.dart'; ...@@ -26,11 +26,13 @@ import 'framework.dart';
/// constraints. /// constraints.
/// {@endtemplate} /// {@endtemplate}
/// ///
/// AndroidView participates in Flutter's [GestureArena]s, and dispatches touch events to the /// {@template flutter.widgets.platformViews.gestures}
/// Android view iff it won the arena. Specific gestures that should be dispatched to the Android /// The widget participates in Flutter's [GestureArena]s, and dispatches touch events to the
/// view can be specified in [AndroidView.gestureRecognizers]. If /// platform view iff it won the arena. Specific gestures that should be dispatched to the platform
/// [AndroidView.gestureRecognizers] is empty, the gesture will be dispatched to the Android /// view can be specified in the `gestureRecognizers` constructor parameter. If
/// the set of gesture recognizers is empty, a gesture will be dispatched to the platform
/// view iff it was not claimed by any other gesture recognizer. /// view iff it was not claimed by any other gesture recognizer.
/// {@endtemplate}
/// ///
/// The Android view object is created using a [PlatformViewFactory](/javadoc/io/flutter/plugin/platform/PlatformViewFactory.html). /// The Android view object is created using a [PlatformViewFactory](/javadoc/io/flutter/plugin/platform/PlatformViewFactory.html).
/// Plugins can register platform view factories with [PlatformViewRegistry#registerViewFactory](/javadoc/io/flutter/plugin/platform/PlatformViewRegistry.html#registerViewFactory-java.lang.String-io.flutter.plugin.platform.PlatformViewFactory-). /// Plugins can register platform view factories with [PlatformViewRegistry#registerViewFactory](/javadoc/io/flutter/plugin/platform/PlatformViewRegistry.html#registerViewFactory-java.lang.String-io.flutter.plugin.platform.PlatformViewFactory-).
...@@ -103,13 +105,15 @@ class AndroidView extends StatefulWidget { ...@@ -103,13 +105,15 @@ class AndroidView extends StatefulWidget {
/// Which gestures should be forwarded to the Android view. /// Which gestures should be forwarded to the Android view.
/// ///
/// {@template flutter.widgets.platformViews.gestureRecognizersDescHead}
/// The gesture recognizers built by factories in this set participate in the gesture arena for /// The gesture recognizers built by factories in this set participate in the gesture arena for
/// each pointer that was put down on the widget. If any of these recognizers win the /// each pointer that was put down on the widget. If any of these recognizers win the
/// gesture arena, the entire pointer event sequence starting from the pointer down event /// gesture arena, the entire pointer event sequence starting from the pointer down event
/// will be dispatched to the Android view. /// will be dispatched to the platform view.
/// ///
/// When null, an empty set of gesture recognizer factories is used, in which case a pointer event sequence /// When null, an empty set of gesture recognizer factories is used, in which case a pointer event sequence
/// will only be dispatched to the Android view if no other member of the arena claimed it. /// will only be dispatched to the platform view if no other member of the arena claimed it.
/// {@endtemplate}
/// ///
/// For example, with the following setup vertical drags will not be dispatched to the Android /// For example, with the following setup vertical drags will not be dispatched to the Android
/// view as the vertical drag gesture is claimed by the parent [GestureDetector]. /// view as the vertical drag gesture is claimed by the parent [GestureDetector].
...@@ -125,7 +129,7 @@ class AndroidView extends StatefulWidget { ...@@ -125,7 +129,7 @@ class AndroidView extends StatefulWidget {
/// gesture recognizer factory in [gestureRecognizers] e.g: /// gesture recognizer factory in [gestureRecognizers] e.g:
/// ```dart /// ```dart
/// GestureDetector( /// GestureDetector(
/// onVerticalDragStart: (DragStartDetails d) {}, /// onVerticalDragStart: (DragStartDetails details) {},
/// child: SizedBox( /// child: SizedBox(
/// width: 200.0, /// width: 200.0,
/// height: 100.0, /// height: 100.0,
...@@ -141,7 +145,8 @@ class AndroidView extends StatefulWidget { ...@@ -141,7 +145,8 @@ class AndroidView extends StatefulWidget {
/// ) /// )
/// ``` /// ```
/// ///
/// An [AndroidView] can be configured to consume all pointers that were put down in its bounds /// {@template flutter.widgets.platformViews.gestureRecognizersDescFoot}
/// A platform view can be configured to consume all pointers that were put down in its bounds
/// by passing a factory for an [EagerGestureRecognizer] in [gestureRecognizers]. /// by passing a factory for an [EagerGestureRecognizer] in [gestureRecognizers].
/// [EagerGestureRecognizer] is a special gesture recognizer that immediately claims the gesture /// [EagerGestureRecognizer] is a special gesture recognizer that immediately claims the gesture
/// after a pointer down event. /// after a pointer down event.
...@@ -149,7 +154,8 @@ class AndroidView extends StatefulWidget { ...@@ -149,7 +154,8 @@ class AndroidView extends StatefulWidget {
/// The `gestureRecognizers` property must not contain more than one factory with the same [Factory.type]. /// The `gestureRecognizers` property must not contain more than one factory with the same [Factory.type].
/// ///
/// Changing `gestureRecognizers` results in rejection of any active gesture arenas (if the /// Changing `gestureRecognizers` results in rejection of any active gesture arenas (if the
/// Android view is actively participating in an arena). /// platform view is actively participating in an arena).
/// {@endtemplate}
// We use OneSequenceGestureRecognizers as they support gesture arena teams. // We use OneSequenceGestureRecognizers as they support gesture arena teams.
// TODO(amirh): get a list of GestureRecognizers here. // TODO(amirh): get a list of GestureRecognizers here.
// https://github.com/flutter/flutter/issues/20953 // https://github.com/flutter/flutter/issues/20953
...@@ -180,7 +186,12 @@ class AndroidView extends StatefulWidget { ...@@ -180,7 +186,12 @@ class AndroidView extends StatefulWidget {
/// ///
/// {@macro flutter.widgets.platformViews.layout} /// {@macro flutter.widgets.platformViews.layout}
/// ///
/// {@macro flutter.widgets.platformViews.gestures}
///
/// {@macro flutter.widgets.platformViews.lifetime} /// {@macro flutter.widgets.platformViews.lifetime}
///
/// Construction of UIViews is done asynchronously, before the UIView is ready this widget paints
/// nothing while maintaining the same layout constraints.
class UiKitView extends StatefulWidget { class UiKitView extends StatefulWidget {
/// Creates a widget that embeds an iOS view. /// Creates a widget that embeds an iOS view.
/// ///
...@@ -194,6 +205,7 @@ class UiKitView extends StatefulWidget { ...@@ -194,6 +205,7 @@ class UiKitView extends StatefulWidget {
this.layoutDirection, this.layoutDirection,
this.creationParams, this.creationParams,
this.creationParamsCodec, this.creationParamsCodec,
this.gestureRecognizers,
}) : assert(viewType != null), }) : assert(viewType != null),
assert(hitTestBehavior != null), assert(hitTestBehavior != null),
assert(creationParams == null || creationParamsCodec != null), assert(creationParams == null || creationParamsCodec != null),
...@@ -227,6 +239,46 @@ class UiKitView extends StatefulWidget { ...@@ -227,6 +239,46 @@ class UiKitView extends StatefulWidget {
/// This must not be null if [creationParams] is not null. /// This must not be null if [creationParams] is not null.
final MessageCodec<dynamic> creationParamsCodec; final MessageCodec<dynamic> creationParamsCodec;
/// Which gestures should be forwarded to the UIKit view.
///
/// {@macro flutter.widgets.platformViews.gestureRecognizersDescHead}
///
/// For example, with the following setup vertical drags will not be dispatched to the UIKit
/// view as the vertical drag gesture is claimed by the parent [GestureDetector].
/// ```dart
/// GestureDetector(
/// onVerticalDragStart: (DragStartDetails details) {},
/// child: UiKitView(
/// viewType: 'webview',
/// ),
/// )
/// ```
/// To get the [UiKitView] 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: UiKitView(
/// viewType: 'webview',
/// 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;
@override @override
State<UiKitView> createState() => _UiKitViewState(); State<UiKitView> createState() => _UiKitViewState();
} }
...@@ -316,7 +368,6 @@ class _AndroidViewState extends State<AndroidView> { ...@@ -316,7 +368,6 @@ class _AndroidViewState extends State<AndroidView> {
} }
class _UiKitViewState extends State<UiKitView> { class _UiKitViewState extends State<UiKitView> {
int _id;
UiKitViewController _controller; UiKitViewController _controller;
TextDirection _layoutDirection; TextDirection _layoutDirection;
bool _initialized = false; bool _initialized = false;
...@@ -330,8 +381,9 @@ class _UiKitViewState extends State<UiKitView> { ...@@ -330,8 +381,9 @@ class _UiKitViewState extends State<UiKitView> {
return const SizedBox.expand(); return const SizedBox.expand();
} }
return _UiKitPlatformView( return _UiKitPlatformView(
viewId: _id, controller: _controller,
hitTestBehavior: widget.hitTestBehavior, hitTestBehavior: widget.hitTestBehavior,
gestureRecognizers: widget.gestureRecognizers ?? _emptyRecognizersSet,
); );
} }
...@@ -389,9 +441,9 @@ class _UiKitViewState extends State<UiKitView> { ...@@ -389,9 +441,9 @@ class _UiKitViewState extends State<UiKitView> {
} }
Future<void> _createNewUiKitView() async { Future<void> _createNewUiKitView() async {
_id = platformViewsRegistry.getNextPlatformViewId(); final int id = platformViewsRegistry.getNextPlatformViewId();
final UiKitViewController controller = await PlatformViewsService.initUiKitView( final UiKitViewController controller = await PlatformViewsService.initUiKitView(
id: _id, id: id,
viewType: widget.viewType, viewType: widget.viewType,
layoutDirection: _layoutDirection, layoutDirection: _layoutDirection,
creationParams: widget.creationParams, creationParams: widget.creationParams,
...@@ -402,7 +454,7 @@ class _UiKitViewState extends State<UiKitView> { ...@@ -402,7 +454,7 @@ class _UiKitViewState extends State<UiKitView> {
return; return;
} }
if (widget.onPlatformViewCreated != null) { if (widget.onPlatformViewCreated != null) {
widget.onPlatformViewCreated(_id); widget.onPlatformViewCreated(id);
} }
setState(() { _controller = controller; }); setState(() { _controller = controller; });
} }
...@@ -442,26 +494,31 @@ class _AndroidPlatformView extends LeafRenderObjectWidget { ...@@ -442,26 +494,31 @@ class _AndroidPlatformView extends LeafRenderObjectWidget {
class _UiKitPlatformView extends LeafRenderObjectWidget { class _UiKitPlatformView extends LeafRenderObjectWidget {
const _UiKitPlatformView({ const _UiKitPlatformView({
Key key, Key key,
@required this.viewId, @required this.controller,
@required this.hitTestBehavior, @required this.hitTestBehavior,
}) : assert(viewId != null), @required this.gestureRecognizers,
}) : assert(controller != null),
assert(hitTestBehavior != null), assert(hitTestBehavior != null),
assert(gestureRecognizers != null),
super(key: key); super(key: key);
final int viewId; final UiKitViewController controller;
final PlatformViewHitTestBehavior hitTestBehavior; final PlatformViewHitTestBehavior hitTestBehavior;
final Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers;
@override @override
RenderObject createRenderObject(BuildContext context) { RenderObject createRenderObject(BuildContext context) {
return RenderUiKitView( return RenderUiKitView(
viewId: viewId, viewController: controller,
hitTestBehavior: hitTestBehavior, hitTestBehavior: hitTestBehavior,
gestureRecognizers: gestureRecognizers,
); );
} }
@override @override
void updateRenderObject(BuildContext context, RenderUiKitView renderObject) { void updateRenderObject(BuildContext context, RenderUiKitView renderObject) {
renderObject.viewId = viewId; renderObject.viewController = controller;
renderObject.hitTestBehavior = hitTestBehavior; renderObject.hitTestBehavior = hitTestBehavior;
renderObject.updateGestureRecognizers(gestureRecognizers);
} }
} }
...@@ -159,6 +159,9 @@ class FakeIosPlatformViewsController { ...@@ -159,6 +159,9 @@ class FakeIosPlatformViewsController {
// delayed until it completes. // delayed until it completes.
Completer<void> creationDelay; Completer<void> creationDelay;
// Maps a view id to the number of gestures it accepted so fat.
final Map<int, int> gesturesAccepted = <int, int>{};
void registerViewType(String viewType) { void registerViewType(String viewType) {
_registeredViewTypes.add(viewType); _registeredViewTypes.add(viewType);
} }
...@@ -169,6 +172,8 @@ class FakeIosPlatformViewsController { ...@@ -169,6 +172,8 @@ class FakeIosPlatformViewsController {
return _create(call); return _create(call);
case 'dispose': case 'dispose':
return _dispose(call); return _dispose(call);
case 'acceptGesture':
return _acceptGesture(call);
} }
return Future<dynamic>.sync(() => null); return Future<dynamic>.sync(() => null);
} }
...@@ -196,6 +201,14 @@ class FakeIosPlatformViewsController { ...@@ -196,6 +201,14 @@ class FakeIosPlatformViewsController {
} }
_views[id] = FakeUiKitView(id, viewType, creationParams); _views[id] = FakeUiKitView(id, viewType, creationParams);
gesturesAccepted[id] = 0;
return Future<int>.sync(() => null);
}
Future<dynamic> _acceptGesture(MethodCall call) async {
final Map<dynamic, dynamic> args = call.arguments;
final int id = args['id'];
gesturesAccepted[id] += 1;
return Future<int>.sync(() => null); return Future<int>.sync(() => null);
} }
......
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