Skip to content

Commit 333c961

Browse files
author
Chris Yang
authored
Extract common PlatformView functionality: Gesture and PointerEvent (flutter#37497)
1 parent 0cd0c66 commit 333c961

File tree

6 files changed

+434
-71
lines changed

6 files changed

+434
-71
lines changed

packages/flutter/lib/src/rendering/platform_view.dart

+102-66
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,12 @@ Set<Type> _factoriesTypeSet<T>(Set<Factory<T>> factories) {
7575
///
7676
/// * [AndroidView] which is a widget that is used to show an Android view.
7777
/// * [PlatformViewsService] which is a service for controlling platform views.
78-
class RenderAndroidView extends RenderBox {
78+
class RenderAndroidView extends RenderBox with _PlatformViewGestureMixin {
7979

8080
/// Creates a render object for an Android view.
8181
RenderAndroidView({
8282
@required AndroidViewController viewController,
83-
@required this.hitTestBehavior,
83+
@required PlatformViewHitTestBehavior hitTestBehavior,
8484
@required Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers,
8585
}) : assert(viewController != null),
8686
assert(hitTestBehavior != null),
@@ -89,6 +89,7 @@ class RenderAndroidView extends RenderBox {
8989
_motionEventsDispatcher = _MotionEventsDispatcher(globalToLocal, viewController);
9090
updateGestureRecognizers(gestureRecognizers);
9191
_viewController.addOnPlatformViewCreatedListener(_onPlatformViewCreated);
92+
this.hitTestBehavior = hitTestBehavior;
9293
}
9394

9495
_PlatformViewState _state = _PlatformViewState.uninitialized;
@@ -117,11 +118,6 @@ class RenderAndroidView extends RenderBox {
117118
markNeedsSemanticsUpdate();
118119
}
119120

120-
/// How to behave during hit testing.
121-
// The implicit setter is enough here as changing this value will just affect
122-
// any newly arriving events there's nothing we need to invalidate.
123-
PlatformViewHitTestBehavior hitTestBehavior;
124-
125121
/// {@template flutter.rendering.platformView.updateGestureRecognizers}
126122
/// Updates which gestures should be forwarded to the platform view.
127123
///
@@ -139,16 +135,7 @@ class RenderAndroidView extends RenderBox {
139135
/// Any active gesture arena the Android view participates in is rejected when the
140136
/// set of gesture recognizers is changed.
141137
void updateGestureRecognizers(Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers) {
142-
assert(gestureRecognizers != null);
143-
assert(
144-
_factoriesTypeSet(gestureRecognizers).length == gestureRecognizers.length,
145-
'There were multiple gesture recognizer factories for the same type, there must only be a single '
146-
'gesture recognizer factory for each gesture recognizer type.',);
147-
if (_factoryTypesSetEquals(gestureRecognizers, _gestureRecognizer?.gestureRecognizerFactories)) {
148-
return;
149-
}
150-
_gestureRecognizer?.dispose();
151-
_gestureRecognizer = _AndroidViewGestureRecognizer(_motionEventsDispatcher, gestureRecognizers);
138+
_updateGestureRecognizersWithCallBack(gestureRecognizers, _motionEventsDispatcher.handlePointerEvent);
152139
}
153140

154141
@override
@@ -162,8 +149,6 @@ class RenderAndroidView extends RenderBox {
162149

163150
_MotionEventsDispatcher _motionEventsDispatcher;
164151

165-
_AndroidViewGestureRecognizer _gestureRecognizer;
166-
167152
@override
168153
void performResize() {
169154
size = constraints.biggest;
@@ -229,24 +214,6 @@ class RenderAndroidView extends RenderBox {
229214
));
230215
}
231216

232-
@override
233-
bool hitTest(BoxHitTestResult result, { Offset position }) {
234-
if (hitTestBehavior == PlatformViewHitTestBehavior.transparent || !size.contains(position))
235-
return false;
236-
result.add(BoxHitTestEntry(this, position));
237-
return hitTestBehavior == PlatformViewHitTestBehavior.opaque;
238-
}
239-
240-
@override
241-
bool hitTestSelf(Offset position) => hitTestBehavior != PlatformViewHitTestBehavior.transparent;
242-
243-
@override
244-
void handleEvent(PointerEvent event, HitTestEntry entry) {
245-
if (event is PointerDownEvent) {
246-
_gestureRecognizer.addPointer(event);
247-
}
248-
}
249-
250217
@override
251218
void describeSemanticsConfiguration (SemanticsConfiguration config) {
252219
super.describeSemanticsConfiguration(config);
@@ -257,12 +224,6 @@ class RenderAndroidView extends RenderBox {
257224
config.platformViewId = _viewController.id;
258225
}
259226
}
260-
261-
@override
262-
void detach() {
263-
_gestureRecognizer.reset();
264-
super.detach();
265-
}
266227
}
267228

268229
/// A render object for an iOS UIKit UIView.
@@ -486,15 +447,17 @@ class _UiKitViewGestureRecognizer extends OneSequenceGestureRecognizer {
486447
}
487448
}
488449

450+
typedef _HandlePointerEvent = void Function(PointerEvent event);
451+
489452
// This recognizer constructs gesture recognizers from a set of gesture recognizer factories
490-
// it was give, adds all of them to a gesture arena team with the _AndroidViewGestureRecognizer
453+
// it was give, adds all of them to a gesture arena team with the _PlatformViewGestureRecognizer
491454
// as the team captain.
492-
// As long as ta gesture arena is unresolved the recognizer caches all pointer events.
493-
// When the team wins the recognizer sends all the cached point events to the embedded Android view, and
494-
// sets itself to a "forwarding mode" where it will forward any new pointer event to the Android view.
495-
class _AndroidViewGestureRecognizer extends OneSequenceGestureRecognizer {
496-
_AndroidViewGestureRecognizer(
497-
this.dispatcher,
455+
// As long as the gesture arena is unresolved, the recognizer caches all pointer events.
456+
// When the team wins, the recognizer sends all the cached pointer events to `_handlePointerEvent`, and
457+
// sets itself to a "forwarding mode" where it will forward any new pointer event to `_handlePointerEvent`.
458+
class _PlatformViewGestureRecognizer extends OneSequenceGestureRecognizer {
459+
_PlatformViewGestureRecognizer(
460+
_HandlePointerEvent handlePointerEvent,
498461
this.gestureRecognizerFactories, {
499462
PointerDeviceKind kind,
500463
}) : super(kind: kind) {
@@ -505,18 +468,19 @@ class _AndroidViewGestureRecognizer extends OneSequenceGestureRecognizer {
505468
return recognizerFactory.constructor()..team = team;
506469
},
507470
).toSet();
471+
_handlePointerEvent = handlePointerEvent;
508472
}
509473

510-
final _MotionEventsDispatcher dispatcher;
474+
_HandlePointerEvent _handlePointerEvent;
511475

512476
// Maps a pointer to a list of its cached pointer events.
513477
// Before the arena for a pointer is resolved all events are cached here, if we win the arena
514-
// the cached events are dispatched to the view, if we lose the arena we clear the cache for
478+
// the cached events are dispatched to `_handlePointerEvent`, if we lose the arena we clear the cache for
515479
// the pointer.
516480
final Map<int, List<PointerEvent>> cachedEvents = <int, List<PointerEvent>>{};
517481

518482
// Pointer for which we have already won the arena, events for pointers in this set are
519-
// immediately dispatched to the Android view.
483+
// immediately dispatched to `_handlePointerEvent`.
520484
final Set<int> forwardedPointers = <int>{};
521485

522486
// We use OneSequenceGestureRecognizers as they support gesture arena teams.
@@ -534,24 +498,24 @@ class _AndroidViewGestureRecognizer extends OneSequenceGestureRecognizer {
534498
}
535499

536500
@override
537-
String get debugDescription => 'Android view';
501+
String get debugDescription => 'Platform view';
538502

539503
@override
540504
void didStopTrackingLastPointer(int pointer) { }
541505

542506
@override
543507
void handleEvent(PointerEvent event) {
544508
if (!forwardedPointers.contains(event.pointer)) {
545-
cacheEvent(event);
509+
_cacheEvent(event);
546510
} else {
547-
dispatcher.handlePointerEvent(event);
511+
_handlePointerEvent(event);
548512
}
549513
stopTrackingIfPointerNoLongerDown(event);
550514
}
551515

552516
@override
553517
void acceptGesture(int pointer) {
554-
flushPointerCache(pointer);
518+
_flushPointerCache(pointer);
555519
forwardedPointers.add(pointer);
556520
}
557521

@@ -561,15 +525,15 @@ class _AndroidViewGestureRecognizer extends OneSequenceGestureRecognizer {
561525
cachedEvents.remove(pointer);
562526
}
563527

564-
void cacheEvent(PointerEvent event) {
528+
void _cacheEvent(PointerEvent event) {
565529
if (!cachedEvents.containsKey(event.pointer)) {
566530
cachedEvents[event.pointer] = <PointerEvent> [];
567531
}
568532
cachedEvents[event.pointer].add(event);
569533
}
570534

571-
void flushPointerCache(int pointer) {
572-
cachedEvents.remove(pointer)?.forEach(dispatcher.handlePointerEvent);
535+
void _flushPointerCache(int pointer) {
536+
cachedEvents.remove(pointer)?.forEach(_handlePointerEvent);
573537
}
574538

575539
@override
@@ -728,18 +692,24 @@ class _MotionEventsDispatcher {
728692

729693
/// A render object for embedding a platform view.
730694
///
731-
/// [PlatformViewRenderBox] presents a platform view by adding a [PlatformViewLayer] layer, integrates it with the gesture arenas system
732-
/// and adds relevant semantic nodes to the semantics tree.
733-
class PlatformViewRenderBox extends RenderBox {
695+
/// [PlatformViewRenderBox] presents a platform view by adding a [PlatformViewLayer] layer,
696+
/// integrates it with the gesture arenas system and adds relevant semantic nodes to the semantics tree.
697+
class PlatformViewRenderBox extends RenderBox with _PlatformViewGestureMixin {
734698

735699
/// Creating a render object for a [PlatformViewSurface].
736700
///
737701
/// The `controller` parameter must not be null.
738702
PlatformViewRenderBox({
739703
@required PlatformViewController controller,
740-
741-
}) : assert(controller != null && controller.viewId != null && controller.viewId > -1),
742-
_controller = controller;
704+
@required PlatformViewHitTestBehavior hitTestBehavior,
705+
@required Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers,
706+
}) : assert(controller != null && controller.viewId != null && controller.viewId > -1),
707+
assert(hitTestBehavior != null),
708+
assert(gestureRecognizers != null),
709+
_controller = controller {
710+
this.hitTestBehavior = hitTestBehavior;
711+
updateGestureRecognizers(gestureRecognizers);
712+
}
743713

744714
/// Sets the [controller] for this render object.
745715
///
@@ -759,6 +729,19 @@ class PlatformViewRenderBox extends RenderBox {
759729
}
760730
}
761731

732+
/// How to behave during hit testing.
733+
// The implicit setter is enough here as changing this value will just affect
734+
// any newly arriving events there's nothing we need to invalidate.
735+
// PlatformViewHitTestBehavior hitTestBehavior;
736+
737+
/// {@macro flutter.rendering.platformView.updateGestureRecognizers}
738+
///
739+
/// Any active gesture arena the `PlatformView` participates in is rejected when the
740+
/// set of gesture recognizers is changed.
741+
void updateGestureRecognizers(Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers) {
742+
_updateGestureRecognizersWithCallBack(gestureRecognizers, _controller.dispatchPointerEvent);
743+
}
744+
762745
PlatformViewController _controller;
763746

764747
@override
@@ -791,3 +774,56 @@ class PlatformViewRenderBox extends RenderBox {
791774
config.platformViewId = _controller.viewId;
792775
}
793776
}
777+
778+
/// The Mixin handling the pointer events and gestures of a platform view render box.
779+
mixin _PlatformViewGestureMixin on RenderBox {
780+
781+
/// How to behave during hit testing.
782+
// The implicit setter is enough here as changing this value will just affect
783+
// any newly arriving events there's nothing we need to invalidate.
784+
PlatformViewHitTestBehavior hitTestBehavior;
785+
786+
/// {@macro flutter.rendering.platformView.updateGestureRecognizers}
787+
///
788+
/// Any active gesture arena the `PlatformView` participates in is rejected when the
789+
/// set of gesture recognizers is changed.
790+
void _updateGestureRecognizersWithCallBack(Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers, _HandlePointerEvent _handlePointerEvent) {
791+
assert(gestureRecognizers != null);
792+
assert(
793+
_factoriesTypeSet(gestureRecognizers).length == gestureRecognizers.length,
794+
'There were multiple gesture recognizer factories for the same type, there must only be a single '
795+
'gesture recognizer factory for each gesture recognizer type.',);
796+
if (_factoryTypesSetEquals(gestureRecognizers, _gestureRecognizer?.gestureRecognizerFactories)) {
797+
return;
798+
}
799+
_gestureRecognizer?.dispose();
800+
_gestureRecognizer = _PlatformViewGestureRecognizer(_handlePointerEvent, gestureRecognizers);
801+
}
802+
803+
_PlatformViewGestureRecognizer _gestureRecognizer;
804+
805+
@override
806+
bool hitTest(BoxHitTestResult result, { Offset position }) {
807+
if (hitTestBehavior == PlatformViewHitTestBehavior.transparent || !size.contains(position)) {
808+
return false;
809+
}
810+
result.add(BoxHitTestEntry(this, position));
811+
return hitTestBehavior == PlatformViewHitTestBehavior.opaque;
812+
}
813+
814+
@override
815+
bool hitTestSelf(Offset position) => hitTestBehavior != PlatformViewHitTestBehavior.transparent;
816+
817+
@override
818+
void handleEvent(PointerEvent event, HitTestEntry entry) {
819+
if (event is PointerDownEvent) {
820+
_gestureRecognizer.addPointer(event);
821+
}
822+
}
823+
824+
@override
825+
void detach() {
826+
_gestureRecognizer.reset();
827+
super.detach();
828+
}
829+
}

packages/flutter/lib/src/services/platform_views.dart

+4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import 'dart:typed_data';
77
import 'dart:ui';
88

99
import 'package:flutter/foundation.dart';
10+
import 'package:flutter/gestures.dart';
1011

1112
import 'message_codec.dart';
1213
import 'system_channels.dart';
@@ -725,4 +726,7 @@ abstract class PlatformViewController {
725726
///
726727
/// See also [PlatformViewRegistry] which is a helper for managing platform view ids.
727728
int get viewId;
729+
730+
/// Dispatches the `event` to the platform view.
731+
void dispatchPointerEvent(PointerEvent event);
728732
}

packages/flutter/lib/src/widgets/platform_view.dart

+53-3
Original file line numberDiff line numberDiff line change
@@ -603,22 +603,72 @@ class PlatformViewSurface extends LeafRenderObjectWidget {
603603
/// The [controller] must not be null.
604604
const PlatformViewSurface({
605605
@required this.controller,
606-
}) : assert(controller != null);
606+
@required this.hitTestBehavior,
607+
@required this.gestureRecognizers,
608+
}) : assert(controller != null),
609+
assert(hitTestBehavior != null),
610+
assert(gestureRecognizers != null);
607611

608612
/// The controller for the platform view integrated by this [PlatformViewSurface].
609613
///
610614
/// [PlatformViewController] is used for dispatching touch events to the platform view.
611615
/// [PlatformViewController.viewId] identifies the platform view whose contents are painted by this widget.
612616
final PlatformViewController controller;
613617

618+
/// Which gestures should be forwarded to the PlatformView.
619+
///
620+
/// {@macro flutter.widgets.platformViews.gestureRecognizersDescHead}
621+
///
622+
/// For example, with the following setup vertical drags will not be dispatched to the platform view
623+
/// as the vertical drag gesture is claimed by the parent [GestureDetector].
624+
///
625+
/// ```dart
626+
/// GestureDetector(
627+
/// onVerticalDragStart: (DragStartDetails details) {},
628+
/// child: PlatformViewSurface(
629+
/// ),
630+
/// )
631+
/// ```
632+
///
633+
/// To get the [PlatformViewSurface] to claim the vertical drag gestures we can pass a vertical drag
634+
/// gesture recognizer factory in [gestureRecognizers] e.g:
635+
///
636+
/// ```dart
637+
/// GestureDetector(
638+
/// onVerticalDragStart: (DragStartDetails details) {},
639+
/// child: SizedBox(
640+
/// width: 200.0,
641+
/// height: 100.0,
642+
/// child: PlatformViewSurface(
643+
/// gestureRecognizers: <Factory<OneSequenceGestureRecognizer>>[
644+
/// new Factory<OneSequenceGestureRecognizer>(
645+
/// () => new EagerGestureRecognizer(),
646+
/// ),
647+
/// ].toSet(),
648+
/// ),
649+
/// ),
650+
/// )
651+
/// ```
652+
///
653+
/// {@macro flutter.widgets.platformViews.gestureRecognizersDescFoot}
654+
// We use OneSequenceGestureRecognizers as they support gesture arena teams.
655+
// TODO(amirh): get a list of GestureRecognizers here.
656+
// https://github.com/flutter/flutter/issues/20953
657+
final Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers;
658+
659+
/// {@macro flutter.widgets.platformViews.hittestParam}
660+
final PlatformViewHitTestBehavior hitTestBehavior;
661+
614662
@override
615663
RenderObject createRenderObject(BuildContext context) {
616-
return PlatformViewRenderBox(controller: controller);
664+
return PlatformViewRenderBox(controller: controller, gestureRecognizers: gestureRecognizers, hitTestBehavior: hitTestBehavior);
617665
}
618666

619667
@override
620668
void updateRenderObject(BuildContext context, PlatformViewRenderBox renderObject) {
621669
renderObject
622-
..controller = controller;
670+
..controller = controller
671+
..hitTestBehavior = hitTestBehavior
672+
..updateGestureRecognizers(gestureRecognizers);
623673
}
624674
}

0 commit comments

Comments
 (0)