Extract common PlatformView functionality: Gesture and PointerEvent (#37497)
This commit is contained in:
parent
0cd0c66021
commit
333c961884
@ -75,12 +75,12 @@ Set<Type> _factoriesTypeSet<T>(Set<Factory<T>> factories) {
|
|||||||
///
|
///
|
||||||
/// * [AndroidView] which is a widget that is used to show an Android view.
|
/// * [AndroidView] which is a widget that is used to show an Android view.
|
||||||
/// * [PlatformViewsService] which is a service for controlling platform views.
|
/// * [PlatformViewsService] which is a service for controlling platform views.
|
||||||
class RenderAndroidView extends RenderBox {
|
class RenderAndroidView extends RenderBox with _PlatformViewGestureMixin {
|
||||||
|
|
||||||
/// Creates a render object for an Android view.
|
/// Creates a render object for an Android view.
|
||||||
RenderAndroidView({
|
RenderAndroidView({
|
||||||
@required AndroidViewController viewController,
|
@required AndroidViewController viewController,
|
||||||
@required this.hitTestBehavior,
|
@required PlatformViewHitTestBehavior hitTestBehavior,
|
||||||
@required Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers,
|
@required Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers,
|
||||||
}) : assert(viewController != null),
|
}) : assert(viewController != null),
|
||||||
assert(hitTestBehavior != null),
|
assert(hitTestBehavior != null),
|
||||||
@ -89,6 +89,7 @@ class RenderAndroidView extends RenderBox {
|
|||||||
_motionEventsDispatcher = _MotionEventsDispatcher(globalToLocal, viewController);
|
_motionEventsDispatcher = _MotionEventsDispatcher(globalToLocal, viewController);
|
||||||
updateGestureRecognizers(gestureRecognizers);
|
updateGestureRecognizers(gestureRecognizers);
|
||||||
_viewController.addOnPlatformViewCreatedListener(_onPlatformViewCreated);
|
_viewController.addOnPlatformViewCreatedListener(_onPlatformViewCreated);
|
||||||
|
this.hitTestBehavior = hitTestBehavior;
|
||||||
}
|
}
|
||||||
|
|
||||||
_PlatformViewState _state = _PlatformViewState.uninitialized;
|
_PlatformViewState _state = _PlatformViewState.uninitialized;
|
||||||
@ -117,11 +118,6 @@ class RenderAndroidView extends RenderBox {
|
|||||||
markNeedsSemanticsUpdate();
|
markNeedsSemanticsUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// How to behave during hit testing.
|
|
||||||
// The implicit setter is enough here as changing this value will just affect
|
|
||||||
// any newly arriving events there's nothing we need to invalidate.
|
|
||||||
PlatformViewHitTestBehavior hitTestBehavior;
|
|
||||||
|
|
||||||
/// {@template flutter.rendering.platformView.updateGestureRecognizers}
|
/// {@template flutter.rendering.platformView.updateGestureRecognizers}
|
||||||
/// Updates which gestures should be forwarded to the platform view.
|
/// Updates which gestures should be forwarded to the platform view.
|
||||||
///
|
///
|
||||||
@ -139,16 +135,7 @@ class RenderAndroidView extends RenderBox {
|
|||||||
/// Any active gesture arena the Android view participates in is rejected when the
|
/// Any active gesture arena the Android view participates in is rejected when the
|
||||||
/// set of gesture recognizers is changed.
|
/// set of gesture recognizers is changed.
|
||||||
void updateGestureRecognizers(Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers) {
|
void updateGestureRecognizers(Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers) {
|
||||||
assert(gestureRecognizers != null);
|
_updateGestureRecognizersWithCallBack(gestureRecognizers, _motionEventsDispatcher.handlePointerEvent);
|
||||||
assert(
|
|
||||||
_factoriesTypeSet(gestureRecognizers).length == gestureRecognizers.length,
|
|
||||||
'There were multiple gesture recognizer factories for the same type, there must only be a single '
|
|
||||||
'gesture recognizer factory for each gesture recognizer type.',);
|
|
||||||
if (_factoryTypesSetEquals(gestureRecognizers, _gestureRecognizer?.gestureRecognizerFactories)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_gestureRecognizer?.dispose();
|
|
||||||
_gestureRecognizer = _AndroidViewGestureRecognizer(_motionEventsDispatcher, gestureRecognizers);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -162,8 +149,6 @@ class RenderAndroidView extends RenderBox {
|
|||||||
|
|
||||||
_MotionEventsDispatcher _motionEventsDispatcher;
|
_MotionEventsDispatcher _motionEventsDispatcher;
|
||||||
|
|
||||||
_AndroidViewGestureRecognizer _gestureRecognizer;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void performResize() {
|
void performResize() {
|
||||||
size = constraints.biggest;
|
size = constraints.biggest;
|
||||||
@ -229,24 +214,6 @@ class RenderAndroidView extends RenderBox {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
bool hitTest(BoxHitTestResult result, { Offset position }) {
|
|
||||||
if (hitTestBehavior == PlatformViewHitTestBehavior.transparent || !size.contains(position))
|
|
||||||
return false;
|
|
||||||
result.add(BoxHitTestEntry(this, position));
|
|
||||||
return hitTestBehavior == PlatformViewHitTestBehavior.opaque;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool hitTestSelf(Offset position) => hitTestBehavior != PlatformViewHitTestBehavior.transparent;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void handleEvent(PointerEvent event, HitTestEntry entry) {
|
|
||||||
if (event is PointerDownEvent) {
|
|
||||||
_gestureRecognizer.addPointer(event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void describeSemanticsConfiguration (SemanticsConfiguration config) {
|
void describeSemanticsConfiguration (SemanticsConfiguration config) {
|
||||||
super.describeSemanticsConfiguration(config);
|
super.describeSemanticsConfiguration(config);
|
||||||
@ -257,12 +224,6 @@ class RenderAndroidView extends RenderBox {
|
|||||||
config.platformViewId = _viewController.id;
|
config.platformViewId = _viewController.id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void detach() {
|
|
||||||
_gestureRecognizer.reset();
|
|
||||||
super.detach();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A render object for an iOS UIKit UIView.
|
/// A render object for an iOS UIKit UIView.
|
||||||
@ -486,15 +447,17 @@ class _UiKitViewGestureRecognizer extends OneSequenceGestureRecognizer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef _HandlePointerEvent = void Function(PointerEvent event);
|
||||||
|
|
||||||
// This recognizer constructs gesture recognizers from a set of gesture recognizer factories
|
// This recognizer constructs gesture recognizers from a set of gesture recognizer factories
|
||||||
// it was give, adds all of them to a gesture arena team with the _AndroidViewGestureRecognizer
|
// it was give, adds all of them to a gesture arena team with the _PlatformViewGestureRecognizer
|
||||||
// as the team captain.
|
// as the team captain.
|
||||||
// As long as ta gesture arena is unresolved the recognizer caches all pointer events.
|
// As long as the gesture arena is unresolved, the recognizer caches all pointer events.
|
||||||
// When the team wins the recognizer sends all the cached point events to the embedded Android view, and
|
// When the team wins, the recognizer sends all the cached pointer events to `_handlePointerEvent`, and
|
||||||
// sets itself to a "forwarding mode" where it will forward any new pointer event to the Android view.
|
// sets itself to a "forwarding mode" where it will forward any new pointer event to `_handlePointerEvent`.
|
||||||
class _AndroidViewGestureRecognizer extends OneSequenceGestureRecognizer {
|
class _PlatformViewGestureRecognizer extends OneSequenceGestureRecognizer {
|
||||||
_AndroidViewGestureRecognizer(
|
_PlatformViewGestureRecognizer(
|
||||||
this.dispatcher,
|
_HandlePointerEvent handlePointerEvent,
|
||||||
this.gestureRecognizerFactories, {
|
this.gestureRecognizerFactories, {
|
||||||
PointerDeviceKind kind,
|
PointerDeviceKind kind,
|
||||||
}) : super(kind: kind) {
|
}) : super(kind: kind) {
|
||||||
@ -505,18 +468,19 @@ class _AndroidViewGestureRecognizer extends OneSequenceGestureRecognizer {
|
|||||||
return recognizerFactory.constructor()..team = team;
|
return recognizerFactory.constructor()..team = team;
|
||||||
},
|
},
|
||||||
).toSet();
|
).toSet();
|
||||||
|
_handlePointerEvent = handlePointerEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
final _MotionEventsDispatcher dispatcher;
|
_HandlePointerEvent _handlePointerEvent;
|
||||||
|
|
||||||
// Maps a pointer to a list of its cached pointer events.
|
// Maps a pointer to a list of its cached pointer events.
|
||||||
// Before the arena for a pointer is resolved all events are cached here, if we win the arena
|
// Before the arena for a pointer is resolved all events are cached here, if we win the arena
|
||||||
// the cached events are dispatched to the view, if we lose the arena we clear the cache for
|
// the cached events are dispatched to `_handlePointerEvent`, if we lose the arena we clear the cache for
|
||||||
// the pointer.
|
// the pointer.
|
||||||
final Map<int, List<PointerEvent>> cachedEvents = <int, List<PointerEvent>>{};
|
final Map<int, List<PointerEvent>> cachedEvents = <int, List<PointerEvent>>{};
|
||||||
|
|
||||||
// Pointer for which we have already won the arena, events for pointers in this set are
|
// Pointer for which we have already won the arena, events for pointers in this set are
|
||||||
// immediately dispatched to the Android view.
|
// immediately dispatched to `_handlePointerEvent`.
|
||||||
final Set<int> forwardedPointers = <int>{};
|
final Set<int> forwardedPointers = <int>{};
|
||||||
|
|
||||||
// We use OneSequenceGestureRecognizers as they support gesture arena teams.
|
// We use OneSequenceGestureRecognizers as they support gesture arena teams.
|
||||||
@ -534,7 +498,7 @@ class _AndroidViewGestureRecognizer extends OneSequenceGestureRecognizer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get debugDescription => 'Android view';
|
String get debugDescription => 'Platform view';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didStopTrackingLastPointer(int pointer) { }
|
void didStopTrackingLastPointer(int pointer) { }
|
||||||
@ -542,16 +506,16 @@ class _AndroidViewGestureRecognizer extends OneSequenceGestureRecognizer {
|
|||||||
@override
|
@override
|
||||||
void handleEvent(PointerEvent event) {
|
void handleEvent(PointerEvent event) {
|
||||||
if (!forwardedPointers.contains(event.pointer)) {
|
if (!forwardedPointers.contains(event.pointer)) {
|
||||||
cacheEvent(event);
|
_cacheEvent(event);
|
||||||
} else {
|
} else {
|
||||||
dispatcher.handlePointerEvent(event);
|
_handlePointerEvent(event);
|
||||||
}
|
}
|
||||||
stopTrackingIfPointerNoLongerDown(event);
|
stopTrackingIfPointerNoLongerDown(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void acceptGesture(int pointer) {
|
void acceptGesture(int pointer) {
|
||||||
flushPointerCache(pointer);
|
_flushPointerCache(pointer);
|
||||||
forwardedPointers.add(pointer);
|
forwardedPointers.add(pointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -561,15 +525,15 @@ class _AndroidViewGestureRecognizer extends OneSequenceGestureRecognizer {
|
|||||||
cachedEvents.remove(pointer);
|
cachedEvents.remove(pointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cacheEvent(PointerEvent event) {
|
void _cacheEvent(PointerEvent event) {
|
||||||
if (!cachedEvents.containsKey(event.pointer)) {
|
if (!cachedEvents.containsKey(event.pointer)) {
|
||||||
cachedEvents[event.pointer] = <PointerEvent> [];
|
cachedEvents[event.pointer] = <PointerEvent> [];
|
||||||
}
|
}
|
||||||
cachedEvents[event.pointer].add(event);
|
cachedEvents[event.pointer].add(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
void flushPointerCache(int pointer) {
|
void _flushPointerCache(int pointer) {
|
||||||
cachedEvents.remove(pointer)?.forEach(dispatcher.handlePointerEvent);
|
cachedEvents.remove(pointer)?.forEach(_handlePointerEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -728,18 +692,24 @@ class _MotionEventsDispatcher {
|
|||||||
|
|
||||||
/// A render object for embedding a platform view.
|
/// A render object for embedding a platform view.
|
||||||
///
|
///
|
||||||
/// [PlatformViewRenderBox] presents a platform view by adding a [PlatformViewLayer] layer, integrates it with the gesture arenas system
|
/// [PlatformViewRenderBox] presents a platform view by adding a [PlatformViewLayer] layer,
|
||||||
/// and adds relevant semantic nodes to the semantics tree.
|
/// integrates it with the gesture arenas system and adds relevant semantic nodes to the semantics tree.
|
||||||
class PlatformViewRenderBox extends RenderBox {
|
class PlatformViewRenderBox extends RenderBox with _PlatformViewGestureMixin {
|
||||||
|
|
||||||
/// Creating a render object for a [PlatformViewSurface].
|
/// Creating a render object for a [PlatformViewSurface].
|
||||||
///
|
///
|
||||||
/// The `controller` parameter must not be null.
|
/// The `controller` parameter must not be null.
|
||||||
PlatformViewRenderBox({
|
PlatformViewRenderBox({
|
||||||
@required PlatformViewController controller,
|
@required PlatformViewController controller,
|
||||||
|
@required PlatformViewHitTestBehavior hitTestBehavior,
|
||||||
|
@required Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers,
|
||||||
}) : assert(controller != null && controller.viewId != null && controller.viewId > -1),
|
}) : assert(controller != null && controller.viewId != null && controller.viewId > -1),
|
||||||
_controller = controller;
|
assert(hitTestBehavior != null),
|
||||||
|
assert(gestureRecognizers != null),
|
||||||
|
_controller = controller {
|
||||||
|
this.hitTestBehavior = hitTestBehavior;
|
||||||
|
updateGestureRecognizers(gestureRecognizers);
|
||||||
|
}
|
||||||
|
|
||||||
/// Sets the [controller] for this render object.
|
/// Sets the [controller] for this render object.
|
||||||
///
|
///
|
||||||
@ -759,6 +729,19 @@ class PlatformViewRenderBox extends RenderBox {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// How to behave during hit testing.
|
||||||
|
// The implicit setter is enough here as changing this value will just affect
|
||||||
|
// any newly arriving events there's nothing we need to invalidate.
|
||||||
|
// PlatformViewHitTestBehavior hitTestBehavior;
|
||||||
|
|
||||||
|
/// {@macro flutter.rendering.platformView.updateGestureRecognizers}
|
||||||
|
///
|
||||||
|
/// Any active gesture arena the `PlatformView` participates in is rejected when the
|
||||||
|
/// set of gesture recognizers is changed.
|
||||||
|
void updateGestureRecognizers(Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers) {
|
||||||
|
_updateGestureRecognizersWithCallBack(gestureRecognizers, _controller.dispatchPointerEvent);
|
||||||
|
}
|
||||||
|
|
||||||
PlatformViewController _controller;
|
PlatformViewController _controller;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -791,3 +774,56 @@ class PlatformViewRenderBox extends RenderBox {
|
|||||||
config.platformViewId = _controller.viewId;
|
config.platformViewId = _controller.viewId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The Mixin handling the pointer events and gestures of a platform view render box.
|
||||||
|
mixin _PlatformViewGestureMixin on RenderBox {
|
||||||
|
|
||||||
|
/// How to behave during hit testing.
|
||||||
|
// The implicit setter is enough here as changing this value will just affect
|
||||||
|
// any newly arriving events there's nothing we need to invalidate.
|
||||||
|
PlatformViewHitTestBehavior hitTestBehavior;
|
||||||
|
|
||||||
|
/// {@macro flutter.rendering.platformView.updateGestureRecognizers}
|
||||||
|
///
|
||||||
|
/// Any active gesture arena the `PlatformView` participates in is rejected when the
|
||||||
|
/// set of gesture recognizers is changed.
|
||||||
|
void _updateGestureRecognizersWithCallBack(Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers, _HandlePointerEvent _handlePointerEvent) {
|
||||||
|
assert(gestureRecognizers != null);
|
||||||
|
assert(
|
||||||
|
_factoriesTypeSet(gestureRecognizers).length == gestureRecognizers.length,
|
||||||
|
'There were multiple gesture recognizer factories for the same type, there must only be a single '
|
||||||
|
'gesture recognizer factory for each gesture recognizer type.',);
|
||||||
|
if (_factoryTypesSetEquals(gestureRecognizers, _gestureRecognizer?.gestureRecognizerFactories)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_gestureRecognizer?.dispose();
|
||||||
|
_gestureRecognizer = _PlatformViewGestureRecognizer(_handlePointerEvent, gestureRecognizers);
|
||||||
|
}
|
||||||
|
|
||||||
|
_PlatformViewGestureRecognizer _gestureRecognizer;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool hitTest(BoxHitTestResult result, { Offset position }) {
|
||||||
|
if (hitTestBehavior == PlatformViewHitTestBehavior.transparent || !size.contains(position)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
result.add(BoxHitTestEntry(this, position));
|
||||||
|
return hitTestBehavior == PlatformViewHitTestBehavior.opaque;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool hitTestSelf(Offset position) => hitTestBehavior != PlatformViewHitTestBehavior.transparent;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void handleEvent(PointerEvent event, HitTestEntry entry) {
|
||||||
|
if (event is PointerDownEvent) {
|
||||||
|
_gestureRecognizer.addPointer(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void detach() {
|
||||||
|
_gestureRecognizer.reset();
|
||||||
|
super.detach();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -7,6 +7,7 @@ import 'dart:typed_data';
|
|||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/gestures.dart';
|
||||||
|
|
||||||
import 'message_codec.dart';
|
import 'message_codec.dart';
|
||||||
import 'system_channels.dart';
|
import 'system_channels.dart';
|
||||||
@ -725,4 +726,7 @@ abstract class PlatformViewController {
|
|||||||
///
|
///
|
||||||
/// See also [PlatformViewRegistry] which is a helper for managing platform view ids.
|
/// See also [PlatformViewRegistry] which is a helper for managing platform view ids.
|
||||||
int get viewId;
|
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.
|
/// The [controller] must not be null.
|
||||||
const PlatformViewSurface({
|
const PlatformViewSurface({
|
||||||
@required this.controller,
|
@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].
|
/// 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.
|
/// [PlatformViewController.viewId] identifies the platform view whose contents are painted by this widget.
|
||||||
final PlatformViewController controller;
|
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
|
@override
|
||||||
RenderObject createRenderObject(BuildContext context) {
|
RenderObject createRenderObject(BuildContext context) {
|
||||||
return PlatformViewRenderBox(controller: controller);
|
return PlatformViewRenderBox(controller: controller, gestureRecognizers: gestureRecognizers, hitTestBehavior: hitTestBehavior);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void updateRenderObject(BuildContext context, PlatformViewRenderBox renderObject) {
|
void updateRenderObject(BuildContext context, PlatformViewRenderBox renderObject) {
|
||||||
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
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@ -15,7 +17,16 @@ void main() {
|
|||||||
PlatformViewRenderBox platformViewRenderBox;
|
PlatformViewRenderBox platformViewRenderBox;
|
||||||
setUp((){
|
setUp((){
|
||||||
fakePlatformViewController = FakePlatformViewController(0);
|
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', () {
|
test('layout should size to max constraint', () {
|
||||||
|
@ -8,6 +8,7 @@ import 'package:collection/collection.dart';
|
|||||||
|
|
||||||
import 'package:flutter/painting.dart';
|
import 'package:flutter/painting.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
/// Used in internal testing.
|
/// Used in internal testing.
|
||||||
class FakePlatformViewController extends PlatformViewController {
|
class FakePlatformViewController extends PlatformViewController {
|
||||||
@ -16,10 +17,22 @@ class FakePlatformViewController extends PlatformViewController {
|
|||||||
_id = id;
|
_id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Events that are dispatched;
|
||||||
|
List<PointerEvent> dispatchedPointerEvents = <PointerEvent>[];
|
||||||
|
|
||||||
int _id;
|
int _id;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get viewId => _id;
|
int get viewId => _id;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispatchPointerEvent(PointerEvent event) {
|
||||||
|
dispatchedPointerEvents.add(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearTestingVariables() {
|
||||||
|
dispatchedPointerEvents.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class FakeAndroidPlatformViewsController {
|
class FakeAndroidPlatformViewsController {
|
||||||
|
@ -1676,12 +1676,261 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('PlatformViewSurface should create platform view layer', (WidgetTester tester) async {
|
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);
|
await tester.pumpWidget(surface);
|
||||||
final PlatformViewLayer layer = tester.layers.firstWhere((Layer layer){
|
final PlatformViewLayer layer = tester.layers.firstWhere((Layer layer){
|
||||||
return layer is PlatformViewLayer;
|
return layer is PlatformViewLayer;
|
||||||
});
|
});
|
||||||
expect(layer, isNotNull);
|
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);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user