mirror of
https://github.com/flutter/flutter
synced 2024-10-13 19:52:53 +00:00
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.
|
||||
/// * [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.
|
||||
RenderAndroidView({
|
||||
@required AndroidViewController viewController,
|
||||
@required this.hitTestBehavior,
|
||||
@required PlatformViewHitTestBehavior hitTestBehavior,
|
||||
@required Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers,
|
||||
}) : assert(viewController != null),
|
||||
assert(hitTestBehavior != null),
|
||||
|
@ -89,6 +89,7 @@ class RenderAndroidView extends RenderBox {
|
|||
_motionEventsDispatcher = _MotionEventsDispatcher(globalToLocal, viewController);
|
||||
updateGestureRecognizers(gestureRecognizers);
|
||||
_viewController.addOnPlatformViewCreatedListener(_onPlatformViewCreated);
|
||||
this.hitTestBehavior = hitTestBehavior;
|
||||
}
|
||||
|
||||
_PlatformViewState _state = _PlatformViewState.uninitialized;
|
||||
|
@ -117,11 +118,6 @@ class RenderAndroidView extends RenderBox {
|
|||
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}
|
||||
/// 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
|
||||
/// set of gesture recognizers is changed.
|
||||
void updateGestureRecognizers(Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers) {
|
||||
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 = _AndroidViewGestureRecognizer(_motionEventsDispatcher, gestureRecognizers);
|
||||
_updateGestureRecognizersWithCallBack(gestureRecognizers, _motionEventsDispatcher.handlePointerEvent);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -162,8 +149,6 @@ class RenderAndroidView extends RenderBox {
|
|||
|
||||
_MotionEventsDispatcher _motionEventsDispatcher;
|
||||
|
||||
_AndroidViewGestureRecognizer _gestureRecognizer;
|
||||
|
||||
@override
|
||||
void performResize() {
|
||||
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
|
||||
void describeSemanticsConfiguration (SemanticsConfiguration config) {
|
||||
super.describeSemanticsConfiguration(config);
|
||||
|
@ -257,12 +224,6 @@ class RenderAndroidView extends RenderBox {
|
|||
config.platformViewId = _viewController.id;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void detach() {
|
||||
_gestureRecognizer.reset();
|
||||
super.detach();
|
||||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
// 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 long as ta 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
|
||||
// sets itself to a "forwarding mode" where it will forward any new pointer event to the Android view.
|
||||
class _AndroidViewGestureRecognizer extends OneSequenceGestureRecognizer {
|
||||
_AndroidViewGestureRecognizer(
|
||||
this.dispatcher,
|
||||
// As long as the gesture arena is unresolved, the recognizer caches all pointer events.
|
||||
// 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 `_handlePointerEvent`.
|
||||
class _PlatformViewGestureRecognizer extends OneSequenceGestureRecognizer {
|
||||
_PlatformViewGestureRecognizer(
|
||||
_HandlePointerEvent handlePointerEvent,
|
||||
this.gestureRecognizerFactories, {
|
||||
PointerDeviceKind kind,
|
||||
}) : super(kind: kind) {
|
||||
|
@ -505,18 +468,19 @@ class _AndroidViewGestureRecognizer extends OneSequenceGestureRecognizer {
|
|||
return recognizerFactory.constructor()..team = team;
|
||||
},
|
||||
).toSet();
|
||||
_handlePointerEvent = handlePointerEvent;
|
||||
}
|
||||
|
||||
final _MotionEventsDispatcher dispatcher;
|
||||
_HandlePointerEvent _handlePointerEvent;
|
||||
|
||||
// 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
|
||||
// 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.
|
||||
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
|
||||
// immediately dispatched to the Android view.
|
||||
// immediately dispatched to `_handlePointerEvent`.
|
||||
final Set<int> forwardedPointers = <int>{};
|
||||
|
||||
// We use OneSequenceGestureRecognizers as they support gesture arena teams.
|
||||
|
@ -534,7 +498,7 @@ class _AndroidViewGestureRecognizer extends OneSequenceGestureRecognizer {
|
|||
}
|
||||
|
||||
@override
|
||||
String get debugDescription => 'Android view';
|
||||
String get debugDescription => 'Platform view';
|
||||
|
||||
@override
|
||||
void didStopTrackingLastPointer(int pointer) { }
|
||||
|
@ -542,16 +506,16 @@ class _AndroidViewGestureRecognizer extends OneSequenceGestureRecognizer {
|
|||
@override
|
||||
void handleEvent(PointerEvent event) {
|
||||
if (!forwardedPointers.contains(event.pointer)) {
|
||||
cacheEvent(event);
|
||||
_cacheEvent(event);
|
||||
} else {
|
||||
dispatcher.handlePointerEvent(event);
|
||||
_handlePointerEvent(event);
|
||||
}
|
||||
stopTrackingIfPointerNoLongerDown(event);
|
||||
}
|
||||
|
||||
@override
|
||||
void acceptGesture(int pointer) {
|
||||
flushPointerCache(pointer);
|
||||
_flushPointerCache(pointer);
|
||||
forwardedPointers.add(pointer);
|
||||
}
|
||||
|
||||
|
@ -561,15 +525,15 @@ class _AndroidViewGestureRecognizer extends OneSequenceGestureRecognizer {
|
|||
cachedEvents.remove(pointer);
|
||||
}
|
||||
|
||||
void cacheEvent(PointerEvent event) {
|
||||
void _cacheEvent(PointerEvent event) {
|
||||
if (!cachedEvents.containsKey(event.pointer)) {
|
||||
cachedEvents[event.pointer] = <PointerEvent> [];
|
||||
}
|
||||
cachedEvents[event.pointer].add(event);
|
||||
}
|
||||
|
||||
void flushPointerCache(int pointer) {
|
||||
cachedEvents.remove(pointer)?.forEach(dispatcher.handlePointerEvent);
|
||||
void _flushPointerCache(int pointer) {
|
||||
cachedEvents.remove(pointer)?.forEach(_handlePointerEvent);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -728,18 +692,24 @@ class _MotionEventsDispatcher {
|
|||
|
||||
/// 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
|
||||
/// and adds relevant semantic nodes to the semantics tree.
|
||||
class PlatformViewRenderBox extends RenderBox {
|
||||
/// [PlatformViewRenderBox] presents a platform view by adding a [PlatformViewLayer] layer,
|
||||
/// integrates it with the gesture arenas system and adds relevant semantic nodes to the semantics tree.
|
||||
class PlatformViewRenderBox extends RenderBox with _PlatformViewGestureMixin {
|
||||
|
||||
/// Creating a render object for a [PlatformViewSurface].
|
||||
///
|
||||
/// The `controller` parameter must not be null.
|
||||
PlatformViewRenderBox({
|
||||
@required PlatformViewController controller,
|
||||
|
||||
@required PlatformViewHitTestBehavior hitTestBehavior,
|
||||
@required Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers,
|
||||
}) : 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.
|
||||
///
|
||||
|
@ -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;
|
||||
|
||||
@override
|
||||
|
@ -791,3 +774,56 @@ class PlatformViewRenderBox extends RenderBox {
|
|||
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 '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);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue