diff --git a/packages/flutter/lib/src/rendering/layer.dart b/packages/flutter/lib/src/rendering/layer.dart index 48ca2c70ea..f4533ea5ee 100644 --- a/packages/flutter/lib/src/rendering/layer.dart +++ b/packages/flutter/lib/src/rendering/layer.dart @@ -357,40 +357,6 @@ class TextureLayer extends Layer { S find(Offset regionOffset) => null; } -/// A layer that shows an embedded [UIView](https://developer.apple.com/documentation/uikit/uiview) -/// on iOS. -class PlatformViewLayer extends Layer { - /// Creates a platform view layer. - /// - /// The `rect` and `viewId` parameters must not be null. - PlatformViewLayer({ - @required this.rect, - @required this.viewId, - }): assert(rect != null), assert(viewId != null); - - /// Bounding rectangle of this layer in the global coordinate space. - final Rect rect; - - /// The unique identifier of the UIView displayed on this layer. - /// - /// A UIView with this identifier must have been created by [PlatformViewsServices.initUiKitView]. - final int viewId; - - @override - void addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]) { - final Rect shiftedRect = rect.shift(layerOffset); - builder.addPlatformView( - viewId, - offset: shiftedRect.topLeft, - width: shiftedRect.width, - height: shiftedRect.height, - ); - } - - @override - S find(Offset regionOffset) => null; -} - /// A layer that indicates to the compositor that it should display /// certain performance statistics within it. /// diff --git a/packages/flutter/lib/src/rendering/platform_view.dart b/packages/flutter/lib/src/rendering/platform_view.dart index 3e4303b287..1734c01f92 100644 --- a/packages/flutter/lib/src/rendering/platform_view.dart +++ b/packages/flutter/lib/src/rendering/platform_view.dart @@ -43,10 +43,8 @@ enum _PlatformViewState { /// [RenderAndroidView] is responsible for sizing, displaying and passing touch events to an /// Android [View](https://developer.android.com/reference/android/view/View). /// -/// {@template flutter.rendering.platformView.layout} /// The render object's layout behavior is to fill all available space, the parent of this object must /// provide bounded layout constraints. -/// {@endtemplate} /// /// RenderAndroidView participates in Flutter's [GestureArena]s, and dispatches touch events to the /// Android view iff it won the arena. Specific gestures that should be dispatched to the Android @@ -235,81 +233,6 @@ class RenderAndroidView extends RenderBox { } } -/// This is work in progress, not yet ready to be used, and requires a custom engine build. A render object for an iOS UIKit UIView. -/// -/// [RenderUiKitView] is responsible for sizing and displaying an iOS -/// [UIView](https://developer.apple.com/documentation/uikit/uiview). -/// -/// UIViews are added as sub views of the FlutterView and are composited by Quartz. -/// -/// {@macro flutter.rendering.platformView.layout} -/// -/// See also: -/// * [UiKitView] which is a widget that is used to show a UIView. -/// * [PlatformViewsService] which is a service for controlling platform views. -class RenderUiKitView extends RenderBox { - /// Creates a render object for an iOS UIView. - /// - /// The `viewId` and `hitTestBehavior` parameters must not be null. - RenderUiKitView({ - @required int viewId, - @required this.hitTestBehavior, - }) : assert(viewId != null), - assert(hitTestBehavior != null), - _viewId = viewId; - - - /// The unique identifier of the UIView controlled by this controller. - /// - /// Typically generated by [PlatformViewsRegistry.getNextPlatformViewId], the UIView - /// must have been created by calling [PlatformViewsService.initUiKitView]. - int get viewId => _viewId; - int _viewId; - set viewId(int viewId) { - assert(viewId != null); - _viewId = viewId; - markNeedsPaint(); - } - - /// 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; - - @override - bool get sizedByParent => true; - - @override - bool get alwaysNeedsCompositing => true; - - @override - bool get isRepaintBoundary => true; - - @override - void performResize() { - size = constraints.biggest; - } - - @override - void paint(PaintingContext context, Offset offset) { - context.addLayer(PlatformViewLayer( - rect: offset & size, - viewId: _viewId, - )); - } - - @override - bool hitTest(HitTestResult 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; -} - class _AndroidViewGestureRecognizer extends OneSequenceGestureRecognizer { _AndroidViewGestureRecognizer(this.dispatcher, this.gestureRecognizerFactories) { team = GestureArenaTeam(); diff --git a/packages/flutter/lib/src/services/platform_views.dart b/packages/flutter/lib/src/services/platform_views.dart index de700003e2..b32f182fe6 100644 --- a/packages/flutter/lib/src/services/platform_views.dart +++ b/packages/flutter/lib/src/services/platform_views.dart @@ -91,46 +91,6 @@ class PlatformViewsService { onPlatformViewCreated, ); } - - // TODO(amirh): reference the iOS plugin API for registering a UIView factory once it lands. - /// This is work in progress, not yet ready to be used, and requires a custom engine build. Creates a controller for a new iOS UIView. - /// - /// `id` is an unused unique identifier generated with [platformViewsRegistry]. - /// - /// `viewType` is the identifier of the iOS view type to be created, a - /// factory for this view type must have been registered on the platform side. - /// Platform view factories are typically registered by plugin code. - /// - /// The `id, `viewType, and `layoutDirection` parameters must not be null. - /// If `creationParams` is non null then `cretaionParamsCodec` must not be null. - static Future initUiKitView({ - @required int id, - @required String viewType, - @required TextDirection layoutDirection, - dynamic creationParams, - MessageCodec creationParamsCodec, - }) async { - assert(id != null); - assert(viewType != null); - assert(layoutDirection != null); - assert(creationParams == null || creationParamsCodec != null); - - // TODO(amirh): pass layoutDirection once the system channel supports it. - final Map args = { - 'id': id, - 'viewType': viewType, - }; - if (creationParams != null) { - final ByteData paramsByteData = creationParamsCodec.encodeMessage(creationParams); - args['params'] = Uint8List.view( - paramsByteData.buffer, - 0, - paramsByteData.lengthInBytes, - ); - } - await SystemChannels.platform_views.invokeMethod('create', args); - return UiKitViewController._(id, layoutDirection); - } } /// Properties of an Android pointer. @@ -495,7 +455,8 @@ class AndroidViewController { /// /// The first time a size is set triggers the creation of the Android view. Future setSize(Size size) async { - assert(_state != _AndroidViewState.disposed, 'trying to size a disposed Android View. View id: $id'); + if (_state == _AndroidViewState.disposed) + throw FlutterError('trying to size a disposed Android View. View id: $id'); assert(size != null); assert(!size.isEmpty); @@ -512,7 +473,8 @@ class AndroidViewController { /// Sets the layout direction for the Android view. Future setLayoutDirection(TextDirection layoutDirection) async { - assert(_state != _AndroidViewState.disposed,'trying to set a layout direction for a disposed UIView. View id: $id'); + if (_state == _AndroidViewState.disposed) + throw FlutterError('trying to set a layout direction for a disposed Android View. View id: $id'); if (layoutDirection == _layoutDirection) return; @@ -582,48 +544,3 @@ class AndroidViewController { _state = _AndroidViewState.created; } } - -/// Controls an iOS UIView. -/// -/// Typically created with [PlatformViewsService.initUiKitView]. -class UiKitViewController { - UiKitViewController._( - this.id, - TextDirection layoutDirection, - ) : assert(id != null), - assert(layoutDirection != null), - _layoutDirection = layoutDirection; - - - /// The unique identifier of the iOS view controlled by this controller. - /// - /// This identifer is typically generated by [PlatformViewsRegistry.getNextPlatformViewId]. - final int id; - - bool _debugDisposed = false; - - TextDirection _layoutDirection; - - /// Sets the layout direction for the Android view. - Future setLayoutDirection(TextDirection layoutDirection) async { - assert(!_debugDisposed, 'trying to set a layout direction for a disposed Android View. View id: $id'); - - if (layoutDirection == _layoutDirection) - return; - - assert(layoutDirection != null); - _layoutDirection = layoutDirection; - - // TODO(amirh): invoke the iOS platform views channel direction method once available. - } - - /// Disposes the view. - /// - /// The [UiKitViewController] object is unusable after calling this. - /// The `id` of the platform view cannot be reused after the view is - /// disposed. - Future dispose() async { - _debugDisposed = true; - await SystemChannels.platform_views.invokeMethod('dispose', id); - } -} diff --git a/packages/flutter/lib/src/widgets/platform_view.dart b/packages/flutter/lib/src/widgets/platform_view.dart index b6b0edd02f..7ad64547e5 100644 --- a/packages/flutter/lib/src/widgets/platform_view.dart +++ b/packages/flutter/lib/src/widgets/platform_view.dart @@ -21,10 +21,8 @@ import 'framework.dart'; /// The embedded Android view is painted just like any other Flutter widget and transformations /// apply to it as well. /// -/// {@template flutter.widgets.platformViews.layout} -/// The widget fills all available space, the parent of this object must provide bounded layout +/// The widget fill all available space, the parent of this object must provide bounded layout /// constraints. -/// {@endtemplate} /// /// AndroidView participates in Flutter's [GestureArena]s, and dispatches touch events to the /// Android view iff it won the arena. Specific gestures that should be dispatched to the Android @@ -43,23 +41,19 @@ import 'framework.dart'; /// } /// ``` /// -/// {@template flutter.widgets.platformViews.lifetime} -/// The platform view's lifetime is the same as the lifetime of the [State] object for this widget. +/// The Android view's lifetime is the same as the lifetime of the [State] object for this widget. /// When the [State] is disposed the platform view (and auxiliary resources) are lazily /// released (some resources are immediately released and some by platform garbage collector). /// A stateful widget's state is disposed when the widget is removed from the tree or when it is /// moved within the tree. If the stateful widget has a key and it's only moved relative to its siblings, /// or it has a [GlobalKey] and it's moved within the tree, it will not be disposed. -/// {@endtemplate} class AndroidView extends StatefulWidget { /// Creates a widget that embeds an Android view. /// - /// {@template flutter.widgets.platformViews.constructorParams} /// The `viewType` and `hitTestBehavior` parameters must not be null. - /// {@endtemplate} /// If `creationParams` is not null then `creationParamsCodec` must not be null. AndroidView({ // ignore: prefer_const_constructors_in_immutables - // TODO(aam): Remove lint ignore above once https://dartbug.com/34297 is fixed + // TODO(aam): Remove lint ignore above once dartbug.com/34297 is fixed Key key, @required this.viewType, this.onPlatformViewCreated, @@ -74,32 +68,25 @@ class AndroidView extends StatefulWidget { super(key: key); /// The unique identifier for Android view type to be embedded by this widget. - /// /// A [PlatformViewFactory](/javadoc/io/flutter/plugin/platform/PlatformViewFactory.html) /// for this type must have been registered. /// /// See also: [AndroidView] for an example of registering a platform view factory. final String viewType; - /// {@template flutter.widgets.platformViews.createdParam} - /// Callback to invoke after the platform view has been created. + /// Callback to invoke after the Android view has been created. /// /// May be null. - /// {@endtemplate} final PlatformViewCreatedCallback onPlatformViewCreated; - /// {@template flutter.widgets.platformViews.hittestParam} /// How this widget should behave during hit testing. /// /// This defaults to [PlatformViewHitTestBehavior.opaque]. - /// {@endtemplate} final PlatformViewHitTestBehavior hitTestBehavior; - /// {@template flutter.widgets.platformViews.directionParam} /// The text direction to use for the embedded view. /// /// If this is null, the ambient [Directionality] is used instead. - /// {@endtemplate} final TextDirection layoutDirection; /// Which gestures should be forwarded to the Android view. @@ -170,50 +157,7 @@ class AndroidView extends StatefulWidget { final MessageCodec creationParamsCodec; @override - State createState() => _AndroidViewState(); -} - -// TODO(amirh): describe the embedding mechanism. -/// This is work in progress, not yet ready to be used, and requires a custom engine build. Embeds an iOS view in the Widget hierarchy. -/// -/// Embedding iOS views is an expensive operation and should be avoided when a Flutter -/// equivalent is possible. -/// -/// {@macro flutter.widgets.platformViews.layout} -/// -/// {@macro flutter.widgets.platformViews.lifetime} -class UiKitView extends StatefulWidget { - /// Creates a widget that embeds an iOS view. - /// - /// {@macro flutter.widgets.platformViews.constructorParams} - UiKitView({ // ignore: prefer_const_constructors_in_immutables - // TODO(aam): Remove lint ignore above once https://dartbug.com/34297 is fixed - Key key, - @required this.viewType, - this.onPlatformViewCreated, - this.hitTestBehavior = PlatformViewHitTestBehavior.opaque, - this.layoutDirection, - }) : assert(viewType != null), - assert(hitTestBehavior != null), - super(key: key); - - // TODO(amirh): reference the iOS API doc once avaliable. - /// The unique identifier for iOS view type to be embedded by this widget. - /// - /// A PlatformViewFactory for this type must have been registered. - final String viewType; - - /// {@macro flutter.widgets.platformViews.createdParam} - final PlatformViewCreatedCallback onPlatformViewCreated; - - /// {@macro flutter.widgets.platformViews.hittestParam} - final PlatformViewHitTestBehavior hitTestBehavior; - - /// {@macro flutter.widgets.platformViews.directionParam} - final TextDirection layoutDirection; - - @override - State createState() => _UiKitViewState(); + State createState() => _AndroidViewState(); } class _AndroidViewState extends State { @@ -239,17 +183,19 @@ class _AndroidViewState extends State { return; } _initialized = true; + _layoutDirection = _findLayoutDirection(); _createNewAndroidView(); } @override void didChangeDependencies() { super.didChangeDependencies(); + _initializeOnce(); + final TextDirection newLayoutDirection = _findLayoutDirection(); final bool didChangeLayoutDirection = _layoutDirection != newLayoutDirection; _layoutDirection = newLayoutDirection; - _initializeOnce(); if (didChangeLayoutDirection) { // The native view will update asynchronously, in the meantime we don't want // to block the framework. (so this is intentionally not awaiting). @@ -300,97 +246,6 @@ class _AndroidViewState extends State { } } -class _UiKitViewState extends State { - int _id; - UiKitViewController _controller; - TextDirection _layoutDirection; - bool _initialized = false; - - static final Set> _emptyRecognizersSet = - Set>(); - - @override - Widget build(BuildContext context) { - if (_controller == null) { - return const SizedBox.expand(); - } - return _UiKitPlatformView( - viewId: _id, - hitTestBehavior: widget.hitTestBehavior, - ); - } - - void _initializeOnce() { - if (_initialized) { - return; - } - _initialized = true; - _createNewUiKitView(); - } - - @override - void didChangeDependencies() { - super.didChangeDependencies(); - final TextDirection newLayoutDirection = _findLayoutDirection(); - final bool didChangeLayoutDirection = _layoutDirection != newLayoutDirection; - _layoutDirection = newLayoutDirection; - - _initializeOnce(); - if (didChangeLayoutDirection) { - // The native view will update asynchronously, in the meantime we don't want - // to block the framework. (so this is intentionally not awaiting). - _controller?.setLayoutDirection(_layoutDirection); - } - } - - @override - void didUpdateWidget(UiKitView oldWidget) { - super.didUpdateWidget(oldWidget); - - final TextDirection newLayoutDirection = _findLayoutDirection(); - final bool didChangeLayoutDirection = _layoutDirection != newLayoutDirection; - _layoutDirection = newLayoutDirection; - - if (widget.viewType != oldWidget.viewType) { - _controller?.dispose(); - _createNewUiKitView(); - return; - } - - if (didChangeLayoutDirection) { - _controller?.setLayoutDirection(_layoutDirection); - } - } - - TextDirection _findLayoutDirection() { - assert(widget.layoutDirection != null || debugCheckHasDirectionality(context)); - return widget.layoutDirection ?? Directionality.of(context); - } - - @override - void dispose() { - _controller?.dispose(); - super.dispose(); - } - - Future _createNewUiKitView() async { - _id = platformViewsRegistry.getNextPlatformViewId(); - final UiKitViewController controller = await PlatformViewsService.initUiKitView( - id: _id, - viewType: widget.viewType, - layoutDirection: _layoutDirection, - ); - if (!mounted) { - controller.dispose(); - return; - } - if (widget.onPlatformViewCreated != null) { - widget.onPlatformViewCreated(_id); - } - setState(() { _controller = controller; }); - } -} - class _AndroidPlatformView extends LeafRenderObjectWidget { const _AndroidPlatformView({ Key key, @@ -421,30 +276,3 @@ class _AndroidPlatformView extends LeafRenderObjectWidget { renderObject.updateGestureRecognizers(gestureRecognizers); } } - -class _UiKitPlatformView extends LeafRenderObjectWidget { - const _UiKitPlatformView({ - Key key, - @required this.viewId, - @required this.hitTestBehavior, - }) : assert(viewId != null), - assert(hitTestBehavior != null), - super(key: key); - - final int viewId; - final PlatformViewHitTestBehavior hitTestBehavior; - - @override - RenderObject createRenderObject(BuildContext context) { - return RenderUiKitView( - viewId: viewId, - hitTestBehavior: hitTestBehavior, - ); - } - - @override - void updateRenderObject(BuildContext context, RenderUiKitView renderObject) { - renderObject.viewId = viewId; - renderObject.hitTestBehavior = hitTestBehavior; - } -} diff --git a/packages/flutter/test/services/fake_platform_views.dart b/packages/flutter/test/services/fake_platform_views.dart index 5c2ec1cd42..8ade917380 100644 --- a/packages/flutter/test/services/fake_platform_views.dart +++ b/packages/flutter/test/services/fake_platform_views.dart @@ -6,19 +6,21 @@ import 'dart:async'; import 'dart:typed_data'; import 'package:collection/collection.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/painting.dart'; import 'package:flutter/services.dart'; -class FakeAndroidPlatformViewsController { - FakeAndroidPlatformViewsController() { +class FakePlatformViewsController { + FakePlatformViewsController(this.targetPlatform) : assert(targetPlatform != null) { SystemChannels.platform_views.setMockMethodCallHandler(_onMethodCall); } + final TargetPlatform targetPlatform; - Iterable get views => _views.values; - final Map _views = {}; + Iterable get views => _views.values; + final Map _views = {}; - final Map> motionEvents = >{}; + final Map> motionEvents = >{}; final Set _registeredViewTypes = Set(); @@ -31,6 +33,12 @@ class FakeAndroidPlatformViewsController { } Future _onMethodCall(MethodCall call) { + if (targetPlatform == TargetPlatform.android) + return _onMethodCallAndroid(call); + return Future.sync(() => null); + } + + Future _onMethodCallAndroid(MethodCall call) { switch(call.method) { case 'create': return _create(call); @@ -67,7 +75,7 @@ class FakeAndroidPlatformViewsController { message: 'Trying to create a platform view of unregistered type: $viewType', ); - _views[id] = FakeAndroidPlatformView(id, viewType, Size(width, height), layoutDirection, creationParams); + _views[id] = FakePlatformView(id, viewType, Size(width, height), layoutDirection, creationParams); final int textureId = _textureCounter++; return Future.sync(() => textureId); } @@ -121,9 +129,9 @@ class FakeAndroidPlatformViewsController { } if (!motionEvents.containsKey(id)) - motionEvents[id] = []; + motionEvents[id] = []; - motionEvents[id].add(FakeAndroidMotionEvent(action, pointerIds, pointerOffsets)); + motionEvents[id].add(FakeMotionEvent(action, pointerIds, pointerOffsets)); return Future.sync(() => null); } @@ -144,77 +152,9 @@ class FakeAndroidPlatformViewsController { } } -class FakeIosPlatformViewsController { - FakeIosPlatformViewsController() { - SystemChannels.platform_views.setMockMethodCallHandler(_onMethodCall); - } +class FakePlatformView { - - Iterable get views => _views.values; - final Map _views = {}; - - final Set _registeredViewTypes = Set(); - - // When this completer is non null, the 'create' method channel call will be - // delayed until it completes. - Completer creationDelay; - - void registerViewType(String viewType) { - _registeredViewTypes.add(viewType); - } - - Future _onMethodCall(MethodCall call) { - switch(call.method) { - case 'create': - return _create(call); - case 'dispose': - return _dispose(call); - } - return Future.sync(() => null); - } - - Future _create(MethodCall call) async { - if (creationDelay != null) - await creationDelay.future; - final Map args = call.arguments; - final int id = args['id']; - final String viewType = args['viewType']; - - if (_views.containsKey(id)) { - throw PlatformException( - code: 'error', - message: 'Trying to create an already created platform view, view id: $id', - ); - } - - if (!_registeredViewTypes.contains(viewType)) { - throw PlatformException( - code: 'error', - message: 'Trying to create a platform view of unregistered type: $viewType', - ); - } - - _views[id] = FakeUiKitView(id, viewType); - return Future.sync(() => null); - } - - Future _dispose(MethodCall call) { - final int id = call.arguments; - - if (!_views.containsKey(id)) { - throw PlatformException( - code: 'error', - message: 'Trying to dispose a platform view with unknown id: $id', - ); - } - - _views.remove(id); - return Future.sync(() => null); - } -} - -class FakeAndroidPlatformView { - FakeAndroidPlatformView(this.id, this.type, this.size, this.layoutDirection, [this.creationParams]); + FakePlatformView(this.id, this.type, this.size, this.layoutDirection, [this.creationParams]); final int id; final String type; @@ -224,9 +164,9 @@ class FakeAndroidPlatformView { @override bool operator ==(dynamic other) { - if (other.runtimeType != FakeAndroidPlatformView) + if (other is! FakePlatformView) return false; - final FakeAndroidPlatformView typedOther = other; + final FakePlatformView typedOther = other; return id == typedOther.id && type == typedOther.type && creationParams == typedOther.creationParams && @@ -238,12 +178,12 @@ class FakeAndroidPlatformView { @override String toString() { - return 'FakeAndroidPlatformView(id: $id, type: $type, size: $size, layoutDirection: $layoutDirection, creationParams: $creationParams)'; + return 'FakePlatformView(id: $id, type: $type, size: $size, layoutDirection: $layoutDirection, creationParams: $creationParams)'; } } -class FakeAndroidMotionEvent { - const FakeAndroidMotionEvent(this.action, this.pointerIds, this.pointers); +class FakeMotionEvent { + const FakeMotionEvent(this.action, this.pointerIds, this.pointers); final int action; final List pointers; @@ -252,9 +192,9 @@ class FakeAndroidMotionEvent { @override bool operator ==(dynamic other) { - if (other is! FakeAndroidMotionEvent) + if (other is! FakeMotionEvent) return false; - final FakeAndroidMotionEvent typedOther = other; + final FakeMotionEvent typedOther = other; const ListEquality offsetsEq = ListEquality(); const ListEquality pointersEq = ListEquality(); return pointersEq.equals(pointerIds, typedOther.pointerIds) && @@ -267,30 +207,6 @@ class FakeAndroidMotionEvent { @override String toString() { - return 'FakeAndroidMotionEvent(action: $action, pointerIds: $pointerIds, pointers: $pointers)'; - } -} - -class FakeUiKitView { - FakeUiKitView(this.id, this.type); - - final int id; - final String type; - - @override - bool operator ==(dynamic other) { - if (other.runtimeType != FakeUiKitView) - return false; - final FakeUiKitView typedOther = other; - return id == typedOther.id && - type == typedOther.type; - } - - @override - int get hashCode => hashValues(id, type); - - @override - String toString() { - return 'FakeIosPlatformView(id: $id, type: $type)'; + return 'FakeMotionEvent(action: $action, pointerIds: $pointerIds, pointers: $pointers)'; } } diff --git a/packages/flutter/test/services/platform_views_test.dart b/packages/flutter/test/services/platform_views_test.dart index e7e0336818..b39032a399 100644 --- a/packages/flutter/test/services/platform_views_test.dart +++ b/packages/flutter/test/services/platform_views_test.dart @@ -2,6 +2,7 @@ // 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/painting.dart'; import 'package:flutter/services.dart'; import '../flutter_test_alternative.dart'; @@ -9,11 +10,11 @@ import '../flutter_test_alternative.dart'; import 'fake_platform_views.dart'; void main() { + FakePlatformViewsController viewsController; group('Android', () { - FakeAndroidPlatformViewsController viewsController; setUp(() { - viewsController = FakeAndroidPlatformViewsController(); + viewsController = FakePlatformViewsController(TargetPlatform.android); }); test('create Android view of unregistered type', () async { @@ -37,9 +38,9 @@ void main() { .setSize(const Size(200.0, 300.0)); expect( viewsController.views, - unorderedEquals([ - FakeAndroidPlatformView(0, 'webview', const Size(100.0, 100.0), AndroidViewController.kAndroidLayoutDirectionLtr), - FakeAndroidPlatformView(1, 'webview', const Size(200.0, 300.0), AndroidViewController.kAndroidLayoutDirectionRtl), + unorderedEquals([ + FakePlatformView(0, 'webview', const Size(100.0, 100.0), AndroidViewController.kAndroidLayoutDirectionLtr), + FakePlatformView(1, 'webview', const Size(200.0, 300.0), AndroidViewController.kAndroidLayoutDirectionRtl), ])); }); @@ -64,11 +65,11 @@ void main() { PlatformViewsService.initAndroidView(id: 1, viewType: 'webview', layoutDirection: TextDirection.ltr); await viewController.setSize(const Size(200.0, 300.0)); - await viewController.dispose(); + viewController.dispose(); expect( viewsController.views, - unorderedEquals([ - FakeAndroidPlatformView(0, 'webview', const Size(100.0, 100.0), AndroidViewController.kAndroidLayoutDirectionLtr), + unorderedEquals([ + FakePlatformView(0, 'webview', const Size(100.0, 100.0), AndroidViewController.kAndroidLayoutDirectionLtr), ])); }); @@ -93,9 +94,9 @@ void main() { await viewController.setSize(const Size(500.0, 500.0)); expect( viewsController.views, - unorderedEquals([ - FakeAndroidPlatformView(0, 'webview', const Size(100.0, 100.0), AndroidViewController.kAndroidLayoutDirectionLtr), - FakeAndroidPlatformView(1, 'webview', const Size(500.0, 500.0), AndroidViewController.kAndroidLayoutDirectionLtr), + unorderedEquals([ + FakePlatformView(0, 'webview', const Size(100.0, 100.0), AndroidViewController.kAndroidLayoutDirectionLtr), + FakePlatformView(1, 'webview', const Size(500.0, 500.0), AndroidViewController.kAndroidLayoutDirectionLtr), ])); }); @@ -128,8 +129,8 @@ void main() { await viewController.setSize(const Size(100.0, 100.0)); expect( viewsController.views, - unorderedEquals([ - FakeAndroidPlatformView(0, 'webview', const Size(100.0, 100.0), AndroidViewController.kAndroidLayoutDirectionLtr), + unorderedEquals([ + FakePlatformView(0, 'webview', const Size(100.0, 100.0), AndroidViewController.kAndroidLayoutDirectionLtr), ])); }); @@ -141,88 +142,9 @@ void main() { await viewController.setLayoutDirection(TextDirection.rtl); expect( viewsController.views, - unorderedEquals([ - FakeAndroidPlatformView(0, 'webview', const Size(100.0, 100.0), AndroidViewController.kAndroidLayoutDirectionRtl), + unorderedEquals([ + FakePlatformView(0, 'webview', const Size(100.0, 100.0), AndroidViewController.kAndroidLayoutDirectionRtl), ])); }); }); - - group('iOS', () - { - FakeIosPlatformViewsController viewsController; - setUp(() { - viewsController = FakeIosPlatformViewsController(); - }); - - test('create iOS view of unregistered type', () async { - expect( - () { - return PlatformViewsService.initUiKitView( - id: 0, - viewType: 'web', - layoutDirection: TextDirection.ltr, - ); - }, - throwsA(isInstanceOf()), - ); - }); - - test('create iOS views', () async { - viewsController.registerViewType('webview'); - await PlatformViewsService.initUiKitView( - id: 0, viewType: 'webview', layoutDirection: TextDirection.ltr); - await PlatformViewsService.initUiKitView( - id: 1, viewType: 'webview', layoutDirection: TextDirection.rtl); - expect( - viewsController.views, - unorderedEquals([ - FakeUiKitView(0, 'webview'), - FakeUiKitView(1, 'webview'), - ]), - ); - }); - - test('reuse iOS view id', () async { - viewsController.registerViewType('webview'); - await PlatformViewsService.initUiKitView( - id: 0, - viewType: 'webview', - layoutDirection: TextDirection.ltr, - ); - expect( - () => PlatformViewsService.initUiKitView( - id: 0, viewType: 'web', layoutDirection: TextDirection.ltr), - throwsA(isInstanceOf()), - ); - }); - - test('dispose iOS view', () async { - viewsController.registerViewType('webview'); - await PlatformViewsService.initUiKitView( - id: 0, viewType: 'webview', layoutDirection: TextDirection.ltr); - final UiKitViewController viewController = await PlatformViewsService.initUiKitView( - id: 1, viewType: 'webview', layoutDirection: TextDirection.ltr); - - viewController.dispose(); - expect( - viewsController.views, - unorderedEquals([ - FakeUiKitView(0, 'webview'), - ])); - }); - - test('dispose inexisting iOS view', () async { - viewsController.registerViewType('webview'); - await PlatformViewsService.initUiKitView(id: 0, viewType: 'webview', layoutDirection: TextDirection.ltr); - final UiKitViewController viewController = await PlatformViewsService.initUiKitView( - id: 1, viewType: 'webview', layoutDirection: TextDirection.ltr); - await viewController.dispose(); - expect( - () async { - await viewController.dispose(); - }, - throwsA(isInstanceOf()), - ); - }); - }); } diff --git a/packages/flutter/test/widgets/platform_view_test.dart b/packages/flutter/test/widgets/platform_view_test.dart index d82e9490b0..d1b51d8463 100644 --- a/packages/flutter/test/widgets/platform_view_test.dart +++ b/packages/flutter/test/widgets/platform_view_test.dart @@ -16,418 +16,517 @@ import '../services/fake_platform_views.dart'; void main() { - group('AndroidView', () { - testWidgets('Create Android view', (WidgetTester tester) async { - final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); - final FakeAndroidPlatformViewsController viewsController = FakeAndroidPlatformViewsController(); - viewsController.registerViewType('webview'); + testWidgets('Create Android view', (WidgetTester tester) async { + final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); + final FakePlatformViewsController viewsController = FakePlatformViewsController(TargetPlatform.android); + viewsController.registerViewType('webview'); - await tester.pumpWidget( - Center( - child: SizedBox( - width: 200.0, - height: 100.0, - child: AndroidView(viewType: 'webview', layoutDirection: TextDirection.ltr), + await tester.pumpWidget( + Center( + child: SizedBox( + width: 200.0, + height: 100.0, + child: AndroidView(viewType: 'webview', layoutDirection: TextDirection.ltr), + ), + ), + ); + + expect( + viewsController.views, + unorderedEquals([ + FakePlatformView(currentViewId + 1, 'webview', const Size(200.0, 100.0), AndroidViewController.kAndroidLayoutDirectionLtr) + ]), + ); + }); + + testWidgets('Create Android view with params', (WidgetTester tester) async { + final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); + final FakePlatformViewsController viewsController = FakePlatformViewsController(TargetPlatform.android); + viewsController.registerViewType('webview'); + + await tester.pumpWidget( + Center( + child: SizedBox( + width: 200.0, + height: 100.0, + child: AndroidView( + viewType: 'webview', + layoutDirection: TextDirection.ltr, + creationParams: 'creation parameters', + creationParamsCodec: const StringCodec(), ), ), - ); + ), + ); - expect( - viewsController.views, - unorderedEquals([ - FakeAndroidPlatformView(currentViewId + 1, 'webview', const Size(200.0, 100.0), - AndroidViewController.kAndroidLayoutDirectionLtr) - ]), - ); - }); + final FakePlatformView fakeView = viewsController.views.first; + final Uint8List rawCreationParams = fakeView.creationParams; + final ByteData byteData = ByteData.view( + rawCreationParams.buffer, + rawCreationParams.offsetInBytes, + rawCreationParams.lengthInBytes + ); + final dynamic actualParams = const StringCodec().decodeMessage(byteData); - testWidgets('Create Android view with params', (WidgetTester tester) async { - final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); - final FakeAndroidPlatformViewsController viewsController = FakeAndroidPlatformViewsController(); - viewsController.registerViewType('webview'); + expect(actualParams, 'creation parameters'); + expect( + viewsController.views, + unorderedEquals([ + FakePlatformView(currentViewId + 1, 'webview', const Size(200.0, 100.0), AndroidViewController.kAndroidLayoutDirectionLtr, fakeView.creationParams) + ]), + ); + }); - await tester.pumpWidget( - Center( - child: SizedBox( - width: 200.0, - height: 100.0, - child: AndroidView( - viewType: 'webview', - layoutDirection: TextDirection.ltr, - creationParams: 'creation parameters', - creationParamsCodec: const StringCodec(), - ), - ), + testWidgets('Zero sized Android view is not created', (WidgetTester tester) async { + final FakePlatformViewsController viewsController = FakePlatformViewsController(TargetPlatform.android); + viewsController.registerViewType('webview'); + + await tester.pumpWidget( + Center( + child: SizedBox( + width: 0.0, + height: 0.0, + child: AndroidView(viewType: 'webview', layoutDirection: TextDirection.ltr), ), - ); + ), + ); - final FakeAndroidPlatformView fakeView = viewsController.views.first; - final Uint8List rawCreationParams = fakeView.creationParams; - final ByteData byteData = ByteData.view( - rawCreationParams.buffer, - rawCreationParams.offsetInBytes, - rawCreationParams.lengthInBytes - ); - final dynamic actualParams = const StringCodec().decodeMessage(byteData); + expect( + viewsController.views, + isEmpty, + ); + }); - expect(actualParams, 'creation parameters'); - expect( - viewsController.views, - unorderedEquals([ - FakeAndroidPlatformView(currentViewId + 1, 'webview', const Size(200.0, 100.0), - AndroidViewController.kAndroidLayoutDirectionLtr, fakeView.creationParams) - ]), - ); - }); - - testWidgets('Zero sized Android view is not created', (WidgetTester tester) async { - final FakeAndroidPlatformViewsController viewsController = FakeAndroidPlatformViewsController(); - viewsController.registerViewType('webview'); - - await tester.pumpWidget( - Center( - child: SizedBox( - width: 0.0, - height: 0.0, - child: AndroidView(viewType: 'webview', layoutDirection: TextDirection.ltr), - ), + testWidgets('Resize Android view', (WidgetTester tester) async { + final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); + final FakePlatformViewsController viewsController = FakePlatformViewsController(TargetPlatform.android); + viewsController.registerViewType('webview'); + await tester.pumpWidget( + Center( + child: SizedBox( + width: 200.0, + height: 100.0, + child: AndroidView(viewType: 'webview', layoutDirection: TextDirection.ltr), ), - ); + ), + ); - expect( - viewsController.views, - isEmpty, - ); - }); + viewsController.resizeCompleter = Completer(); - testWidgets('Resize Android view', (WidgetTester tester) async { - final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); - final FakeAndroidPlatformViewsController viewsController = FakeAndroidPlatformViewsController(); - viewsController.registerViewType('webview'); - await tester.pumpWidget( - Center( - child: SizedBox( - width: 200.0, - height: 100.0, - child: AndroidView(viewType: 'webview', layoutDirection: TextDirection.ltr), - ), + await tester.pumpWidget( + Center( + child: SizedBox( + width: 100.0, + height: 50.0, + child: AndroidView(viewType: 'webview', layoutDirection: TextDirection.ltr), ), - ); + ), + ); - viewsController.resizeCompleter = Completer(); + final Layer textureParentLayer = tester.layers[tester.layers.length - 2]; + expect(textureParentLayer, isInstanceOf()); + final ClipRectLayer clipRect = textureParentLayer; + expect(clipRect.clipRect, Rect.fromLTWH(0.0, 0.0, 100.0, 50.0)); + expect( + viewsController.views, + unorderedEquals([ + FakePlatformView(currentViewId + 1, 'webview', const Size(200.0, 100.0), AndroidViewController.kAndroidLayoutDirectionLtr) + ]), + ); - await tester.pumpWidget( - Center( - child: SizedBox( - width: 100.0, - height: 50.0, - child: AndroidView(viewType: 'webview', layoutDirection: TextDirection.ltr), - ), + viewsController.resizeCompleter.complete(); + await tester.pump(); + + expect( + viewsController.views, + unorderedEquals([ + FakePlatformView(currentViewId + 1, 'webview', const Size(100.0, 50.0), AndroidViewController.kAndroidLayoutDirectionLtr) + ]), + ); + }); + + testWidgets('Change Android view type', (WidgetTester tester) async { + final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); + final FakePlatformViewsController viewsController = FakePlatformViewsController(TargetPlatform.android); + viewsController.registerViewType('webview'); + viewsController.registerViewType('maps'); + await tester.pumpWidget( + Center( + child: SizedBox( + width: 200.0, + height: 100.0, + child: AndroidView(viewType: 'webview', layoutDirection: TextDirection.ltr), ), - ); + ), + ); - final Layer textureParentLayer = tester.layers[tester.layers.length - 2]; - expect(textureParentLayer, isInstanceOf()); - final ClipRectLayer clipRect = textureParentLayer; - expect(clipRect.clipRect, Rect.fromLTWH(0.0, 0.0, 100.0, 50.0)); - expect( - viewsController.views, - unorderedEquals([ - FakeAndroidPlatformView(currentViewId + 1, 'webview', const Size(200.0, 100.0), - AndroidViewController.kAndroidLayoutDirectionLtr) - ]), - ); - - viewsController.resizeCompleter.complete(); - await tester.pump(); - - expect( - viewsController.views, - unorderedEquals([ - FakeAndroidPlatformView(currentViewId + 1, 'webview', const Size(100.0, 50.0), - AndroidViewController.kAndroidLayoutDirectionLtr) - ]), - ); - }); - - testWidgets('Change Android view type', (WidgetTester tester) async { - final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); - final FakeAndroidPlatformViewsController viewsController = FakeAndroidPlatformViewsController(); - viewsController.registerViewType('webview'); - viewsController.registerViewType('maps'); - await tester.pumpWidget( - Center( - child: SizedBox( - width: 200.0, - height: 100.0, - child: AndroidView(viewType: 'webview', layoutDirection: TextDirection.ltr), - ), + await tester.pumpWidget( + Center( + child: SizedBox( + width: 200.0, + height: 100.0, + child: AndroidView(viewType: 'maps', layoutDirection: TextDirection.ltr), ), - ); + ), + ); - await tester.pumpWidget( - Center( - child: SizedBox( - width: 200.0, - height: 100.0, - child: AndroidView(viewType: 'maps', layoutDirection: TextDirection.ltr), - ), + expect( + viewsController.views, + unorderedEquals([ + FakePlatformView(currentViewId + 2, 'maps', const Size(200.0, 100.0), AndroidViewController.kAndroidLayoutDirectionLtr) + ]), + ); + }); + + testWidgets('Dispose Android view', (WidgetTester tester) async { + final FakePlatformViewsController viewsController = FakePlatformViewsController(TargetPlatform.android); + viewsController.registerViewType('webview'); + await tester.pumpWidget( + Center( + child: SizedBox( + width: 200.0, + height: 100.0, + child: AndroidView(viewType: 'webview', layoutDirection: TextDirection.ltr), ), - ); + ), + ); - expect( - viewsController.views, - unorderedEquals([ - FakeAndroidPlatformView(currentViewId + 2, 'maps', const Size(200.0, 100.0), - AndroidViewController.kAndroidLayoutDirectionLtr) - ]), - ); - }); - - testWidgets('Dispose Android view', (WidgetTester tester) async { - final FakeAndroidPlatformViewsController viewsController = FakeAndroidPlatformViewsController(); - viewsController.registerViewType('webview'); - await tester.pumpWidget( - Center( - child: SizedBox( - width: 200.0, - height: 100.0, - child: AndroidView(viewType: 'webview', layoutDirection: TextDirection.ltr), - ), + await tester.pumpWidget( + const Center( + child: SizedBox( + width: 200.0, + height: 100.0, ), - ); + ), + ); - await tester.pumpWidget( - const Center( - child: SizedBox( - width: 200.0, - height: 100.0, - ), + expect( + viewsController.views, + isEmpty, + ); + }); + + testWidgets('Android view survives widget tree change', (WidgetTester tester) async { + final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); + final FakePlatformViewsController viewsController = FakePlatformViewsController(TargetPlatform.android); + viewsController.registerViewType('webview'); + final GlobalKey key = GlobalKey(); + await tester.pumpWidget( + Center( + child: SizedBox( + width: 200.0, + height: 100.0, + child: AndroidView(viewType: 'webview', layoutDirection: TextDirection.ltr, key: key), ), - ); + ), + ); - expect( - viewsController.views, - isEmpty, - ); - }); - - testWidgets('Android view survives widget tree change', (WidgetTester tester) async { - final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); - final FakeAndroidPlatformViewsController viewsController = FakeAndroidPlatformViewsController(); - viewsController.registerViewType('webview'); - final GlobalKey key = GlobalKey(); - await tester.pumpWidget( - Center( + await tester.pumpWidget( + Center( + child: Container( child: SizedBox( width: 200.0, height: 100.0, child: AndroidView(viewType: 'webview', layoutDirection: TextDirection.ltr, key: key), ), ), - ); + ), + ); - await tester.pumpWidget( - Center( - child: Container( - child: SizedBox( - width: 200.0, - height: 100.0, - child: AndroidView(viewType: 'webview', layoutDirection: TextDirection.ltr, key: key), - ), - ), + expect( + viewsController.views, + unorderedEquals([ + FakePlatformView(currentViewId + 1, 'webview', const Size(200.0, 100.0), AndroidViewController.kAndroidLayoutDirectionLtr) + ]), + ); + }); + + testWidgets('Android view gets touch events', (WidgetTester tester) async { + final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); + final FakePlatformViewsController viewsController = FakePlatformViewsController(TargetPlatform.android); + viewsController.registerViewType('webview'); + await tester.pumpWidget( + Align( + alignment: Alignment.topLeft, + child: SizedBox( + width: 200.0, + height: 100.0, + child: AndroidView(viewType: 'webview', layoutDirection: TextDirection.ltr,), ), - ); + ), + ); - expect( - viewsController.views, - unorderedEquals([ - FakeAndroidPlatformView(currentViewId + 1, 'webview', const Size(200.0, 100.0), - AndroidViewController.kAndroidLayoutDirectionLtr) - ]), - ); - }); + final TestGesture gesture = await tester.startGesture(const Offset(50.0, 50.0)); + await gesture.up(); - testWidgets('Android view gets touch events', (WidgetTester tester) async { - final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); - final FakeAndroidPlatformViewsController viewsController = FakeAndroidPlatformViewsController(); - viewsController.registerViewType('webview'); - await tester.pumpWidget( - Align( - alignment: Alignment.topLeft, + expect( + viewsController.motionEvents[currentViewId + 1], + orderedEquals([ + const FakeMotionEvent(AndroidViewController.kActionDown, [0], [Offset(50.0, 50.0)]), + const FakeMotionEvent(AndroidViewController.kActionUp, [0], [Offset(50.0, 50.0)]), + ]), + ); + }); + + testWidgets('Android view transparent hit test behavior', (WidgetTester tester) async { + final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); + final FakePlatformViewsController viewsController = FakePlatformViewsController(TargetPlatform.android); + viewsController.registerViewType('webview'); + + int numPointerDownsOnParent = 0; + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: Stack( + children: [ + Listener( + behavior: HitTestBehavior.opaque, + onPointerDown: (PointerDownEvent e) { numPointerDownsOnParent++; }, + ), + Positioned( + child: SizedBox( + width: 200.0, + height: 100.0, + child: AndroidView( + viewType: 'webview', + hitTestBehavior: PlatformViewHitTestBehavior.transparent, + layoutDirection: TextDirection.ltr, + ), + ), + ), + ], + ), + ), + ); + + await tester.startGesture(const Offset(50.0, 50.0)); + + expect( + viewsController.motionEvents[currentViewId + 1], + isNull, + ); + expect( + numPointerDownsOnParent, + 1 + ); + }); + + testWidgets('Android view translucent hit test behavior', (WidgetTester tester) async { + final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); + final FakePlatformViewsController viewsController = FakePlatformViewsController(TargetPlatform.android); + viewsController.registerViewType('webview'); + + int numPointerDownsOnParent = 0; + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: Stack( + children: [ + Listener( + behavior: HitTestBehavior.opaque, + onPointerDown: (PointerDownEvent e) { numPointerDownsOnParent++; }, + ), + Positioned( + child: SizedBox( + width: 200.0, + height: 100.0, + child: AndroidView( + viewType: 'webview', + hitTestBehavior: PlatformViewHitTestBehavior.translucent, + layoutDirection: TextDirection.ltr, + ), + ), + ), + ], + ), + ), + ); + + await tester.startGesture(const Offset(50.0, 50.0)); + + expect( + viewsController.motionEvents[currentViewId + 1], + orderedEquals([ + const FakeMotionEvent(AndroidViewController.kActionDown, [0], [Offset(50.0, 50.0)]), + ]), + ); + expect( + numPointerDownsOnParent, + 1 + ); + }); + + testWidgets('Android view opaque hit test behavior', (WidgetTester tester) async { + final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); + final FakePlatformViewsController viewsController = FakePlatformViewsController(TargetPlatform.android); + viewsController.registerViewType('webview'); + + int numPointerDownsOnParent = 0; + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: Stack( + children: [ + Listener( + behavior: HitTestBehavior.opaque, + onPointerDown: (PointerDownEvent e) { numPointerDownsOnParent++; }, + ), + Positioned( + child: SizedBox( + width: 200.0, + height: 100.0, + child: AndroidView( + viewType: 'webview', + hitTestBehavior: PlatformViewHitTestBehavior.opaque, + layoutDirection: TextDirection.ltr, + ), + ), + ), + ], + ), + ), + ); + + await tester.startGesture(const Offset(50.0, 50.0)); + + expect( + viewsController.motionEvents[currentViewId + 1], + orderedEquals([ + const FakeMotionEvent(AndroidViewController.kActionDown, [0], [Offset(50.0, 50.0)]), + ]), + ); + expect( + numPointerDownsOnParent, + 0 + ); + }); + + testWidgets('Android view touch events are in virtual display\'s coordinate system', (WidgetTester tester) async { + final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); + final FakePlatformViewsController viewsController = FakePlatformViewsController(TargetPlatform.android); + viewsController.registerViewType('webview'); + await tester.pumpWidget( + Align( + alignment: Alignment.topLeft, + child: Container( + margin: const EdgeInsets.all(10.0), child: SizedBox( width: 200.0, height: 100.0, - child: AndroidView(viewType: 'webview', layoutDirection: TextDirection.ltr,), + child: AndroidView(viewType: 'webview', layoutDirection: TextDirection.ltr), ), ), - ); + ), + ); - final TestGesture gesture = await tester.startGesture(const Offset(50.0, 50.0)); - await gesture.up(); + final TestGesture gesture = await tester.startGesture(const Offset(50.0, 50.0)); + await gesture.up(); - expect( - viewsController.motionEvents[currentViewId + 1], - orderedEquals([ - const FakeAndroidMotionEvent( - AndroidViewController.kActionDown, [0], [Offset(50.0, 50.0)]), - const FakeAndroidMotionEvent( - AndroidViewController.kActionUp, [0], [Offset(50.0, 50.0)]), - ]), - ); - }); + expect( + viewsController.motionEvents[currentViewId + 1], + orderedEquals([ + const FakeMotionEvent(AndroidViewController.kActionDown, [0], [Offset(40.0, 40.0)]), + const FakeMotionEvent(AndroidViewController.kActionUp, [0], [Offset(40.0, 40.0)]), + ]), + ); + }); - testWidgets('Android view transparent hit test behavior', (WidgetTester tester) async { - final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); - final FakeAndroidPlatformViewsController viewsController = FakeAndroidPlatformViewsController(); - viewsController.registerViewType('webview'); + testWidgets('Android view directionality', (WidgetTester tester) async { + final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); + final FakePlatformViewsController viewsController = FakePlatformViewsController(TargetPlatform.android); + viewsController.registerViewType('maps'); + await tester.pumpWidget( + Center( + child: SizedBox( + width: 200.0, + height: 100.0, + child: AndroidView(viewType: 'maps', layoutDirection: TextDirection.rtl), + ), + ), + ); - int numPointerDownsOnParent = 0; - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: Stack( - children: [ - Listener( - behavior: HitTestBehavior.opaque, - onPointerDown: (PointerDownEvent e) { - numPointerDownsOnParent++; - }, - ), - Positioned( - child: SizedBox( - width: 200.0, - height: 100.0, - child: AndroidView( - viewType: 'webview', - hitTestBehavior: PlatformViewHitTestBehavior.transparent, - layoutDirection: TextDirection.ltr, - ), - ), - ), - ], + expect( + viewsController.views, + unorderedEquals([ + FakePlatformView(currentViewId + 1, 'maps', const Size(200.0, 100.0), AndroidViewController.kAndroidLayoutDirectionRtl) + ]), + ); + + await tester.pumpWidget( + Center( + child: SizedBox( + width: 200.0, + height: 100.0, + child: AndroidView(viewType: 'maps', layoutDirection: TextDirection.ltr), + ), + ), + ); + + expect( + viewsController.views, + unorderedEquals([ + FakePlatformView(currentViewId + 1, 'maps', const Size(200.0, 100.0), AndroidViewController.kAndroidLayoutDirectionLtr) + ]), + ); + }); + + testWidgets('Android view ambient directionality', (WidgetTester tester) async { + final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); + final FakePlatformViewsController viewsController = FakePlatformViewsController(TargetPlatform.android); + viewsController.registerViewType('maps'); + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.rtl, + child: Center( + child: SizedBox( + width: 200.0, + height: 100.0, + child: AndroidView(viewType: 'maps'), ), ), - ); + ), + ); - await tester.startGesture(const Offset(50.0, 50.0)); + expect( + viewsController.views, + unorderedEquals([ + FakePlatformView(currentViewId + 1, 'maps', const Size(200.0, 100.0), AndroidViewController.kAndroidLayoutDirectionRtl) + ]), + ); - expect( - viewsController.motionEvents[currentViewId + 1], - isNull, - ); - expect( - numPointerDownsOnParent, - 1 - ); - }); - - testWidgets('Android view translucent hit test behavior', (WidgetTester tester) async { - final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); - final FakeAndroidPlatformViewsController viewsController = FakeAndroidPlatformViewsController(); - viewsController.registerViewType('webview'); - - int numPointerDownsOnParent = 0; - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: Stack( - children: [ - Listener( - behavior: HitTestBehavior.opaque, - onPointerDown: (PointerDownEvent e) { - numPointerDownsOnParent++; - }, - ), - Positioned( - child: SizedBox( - width: 200.0, - height: 100.0, - child: AndroidView( - viewType: 'webview', - hitTestBehavior: PlatformViewHitTestBehavior.translucent, - layoutDirection: TextDirection.ltr, - ), - ), - ), - ], + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: Center( + child: SizedBox( + width: 200.0, + height: 100.0, + child: AndroidView(viewType: 'maps'), ), ), - ); + ), + ); - await tester.startGesture(const Offset(50.0, 50.0)); + expect( + viewsController.views, + unorderedEquals([ + FakePlatformView(currentViewId + 1, 'maps', const Size(200.0, 100.0), AndroidViewController.kAndroidLayoutDirectionLtr) + ]), + ); + }); - expect( - viewsController.motionEvents[currentViewId + 1], - orderedEquals([ - const FakeAndroidMotionEvent( - AndroidViewController.kActionDown, [0], [Offset(50.0, 50.0)]), - ]), - ); - expect( - numPointerDownsOnParent, - 1 - ); - }); - - testWidgets('Android view opaque hit test behavior', (WidgetTester tester) async { - final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); - final FakeAndroidPlatformViewsController viewsController = FakeAndroidPlatformViewsController(); - viewsController.registerViewType('webview'); - - int numPointerDownsOnParent = 0; - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: Stack( - children: [ - Listener( - behavior: HitTestBehavior.opaque, - onPointerDown: (PointerDownEvent e) { - numPointerDownsOnParent++; - }, - ), - Positioned( - child: SizedBox( - width: 200.0, - height: 100.0, - child: AndroidView( - viewType: 'webview', - hitTestBehavior: PlatformViewHitTestBehavior.opaque, - layoutDirection: TextDirection.ltr, - ), - ), - ), - ], - ), - ), - ); - - await tester.startGesture(const Offset(50.0, 50.0)); - - expect( - viewsController.motionEvents[currentViewId + 1], - orderedEquals([ - const FakeAndroidMotionEvent( - AndroidViewController.kActionDown, [0], [Offset(50.0, 50.0)]), - ]), - ); - expect( - numPointerDownsOnParent, - 0 - ); - }); - - testWidgets('Android view touch events are in virtual display\'s coordinate system', ( - WidgetTester tester) async { - final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); - final FakeAndroidPlatformViewsController viewsController = FakeAndroidPlatformViewsController(); - viewsController.registerViewType('webview'); - await tester.pumpWidget( - Align( - alignment: Alignment.topLeft, - child: Container( - margin: const EdgeInsets.all(10.0), + testWidgets('Android view can lose gesture arenas', (WidgetTester tester) async { + final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); + final FakePlatformViewsController viewsController = FakePlatformViewsController(TargetPlatform.android); + viewsController.registerViewType('webview'); + 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, @@ -435,242 +534,75 @@ void main() { ), ), ), - ); + ), + ); - final TestGesture gesture = await tester.startGesture(const Offset(50.0, 50.0)); - await gesture.up(); + 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( - viewsController.motionEvents[currentViewId + 1], - orderedEquals([ - const FakeAndroidMotionEvent( - AndroidViewController.kActionDown, [0], [Offset(40.0, 40.0)]), - const FakeAndroidMotionEvent( - AndroidViewController.kActionUp, [0], [Offset(40.0, 40.0)]), - ]), - ); - }); + expect(verticalDragAcceptedByParent, true); + expect( + viewsController.motionEvents[currentViewId + 1], + isNull, + ); + }); - testWidgets('Android view directionality', (WidgetTester tester) async { - final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); - final FakeAndroidPlatformViewsController viewsController = FakeAndroidPlatformViewsController(); - viewsController.registerViewType('maps'); - await tester.pumpWidget( - Center( + testWidgets('Android view gesture recognizers', (WidgetTester tester) async { + final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); + final FakePlatformViewsController viewsController = FakePlatformViewsController(TargetPlatform.android); + viewsController.registerViewType('webview'); + 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: AndroidView(viewType: 'maps', layoutDirection: TextDirection.rtl), - ), - ), - ); - - expect( - viewsController.views, - unorderedEquals([ - FakeAndroidPlatformView(currentViewId + 1, 'maps', const Size(200.0, 100.0), - AndroidViewController.kAndroidLayoutDirectionRtl) - ]), - ); - - await tester.pumpWidget( - Center( - child: SizedBox( - width: 200.0, - height: 100.0, - child: AndroidView(viewType: 'maps', layoutDirection: TextDirection.ltr), - ), - ), - ); - - expect( - viewsController.views, - unorderedEquals([ - FakeAndroidPlatformView(currentViewId + 1, 'maps', const Size(200.0, 100.0), - AndroidViewController.kAndroidLayoutDirectionLtr) - ]), - ); - }); - - testWidgets('Android view ambient directionality', (WidgetTester tester) async { - final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); - final FakeAndroidPlatformViewsController viewsController = FakeAndroidPlatformViewsController(); - viewsController.registerViewType('maps'); - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.rtl, - child: Center( - child: SizedBox( - width: 200.0, - height: 100.0, - child: AndroidView(viewType: 'maps'), + child: AndroidView( + viewType: 'webview', + gestureRecognizers: >[ + Factory( + () => VerticalDragGestureRecognizer(), + ), + ].toSet(), + layoutDirection: TextDirection.ltr, ), ), ), - ); + ), + ); - expect( - viewsController.views, - unorderedEquals([ - FakeAndroidPlatformView(currentViewId + 1, 'maps', const Size(200.0, 100.0), - AndroidViewController.kAndroidLayoutDirectionRtl) - ]), - ); + final TestGesture gesture = await tester.startGesture(const Offset(50.0, 50.0)); + await gesture.moveBy(const Offset(0.0, 100.0)); + await gesture.up(); - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: Center( - child: SizedBox( - width: 200.0, - height: 100.0, - child: AndroidView(viewType: 'maps'), - ), - ), - ), - ); + expect(verticalDragAcceptedByParent, false); + expect( + viewsController.motionEvents[currentViewId + 1], + orderedEquals([ + const FakeMotionEvent(AndroidViewController.kActionDown, [0], [Offset(50.0, 50.0)]), + const FakeMotionEvent(AndroidViewController.kActionMove, [0], [Offset(50.0, 150.0)]), + const FakeMotionEvent(AndroidViewController.kActionUp, [0], [Offset(50.0, 150.0)]), + ]), + ); + }); - expect( - viewsController.views, - unorderedEquals([ - FakeAndroidPlatformView(currentViewId + 1, 'maps', const Size(200.0, 100.0), - AndroidViewController.kAndroidLayoutDirectionLtr) - ]), - ); - }); - - testWidgets('Android view can lose gesture arenas', (WidgetTester tester) async { - final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); - final FakeAndroidPlatformViewsController viewsController = FakeAndroidPlatformViewsController(); - viewsController.registerViewType('webview'); - 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: AndroidView(viewType: 'webview', layoutDirection: TextDirection.ltr), - ), - ), - ), - ), - ); - - 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( - viewsController.motionEvents[currentViewId + 1], - isNull, - ); - }); - - testWidgets('Android view gesture recognizers', (WidgetTester tester) async { - final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); - final FakeAndroidPlatformViewsController viewsController = FakeAndroidPlatformViewsController(); - viewsController.registerViewType('webview'); - 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: AndroidView( - viewType: 'webview', - gestureRecognizers: >[ - Factory( - () => VerticalDragGestureRecognizer(), - ), - ].toSet(), - layoutDirection: TextDirection.ltr, - ), - ), - ), - ), - ); - - 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( - viewsController.motionEvents[currentViewId + 1], - orderedEquals([ - const FakeAndroidMotionEvent( - AndroidViewController.kActionDown, [0], [Offset(50.0, 50.0)]), - const FakeAndroidMotionEvent( - AndroidViewController.kActionMove, [0], [Offset(50.0, 150.0)]), - const FakeAndroidMotionEvent( - AndroidViewController.kActionUp, [0], [Offset(50.0, 150.0)]), - ]), - ); - }); - - testWidgets( - 'Android view can claim gesture after all pointers are up', (WidgetTester tester) async { - final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); - final FakeAndroidPlatformViewsController viewsController = FakeAndroidPlatformViewsController(); - viewsController.registerViewType('webview'); - bool verticalDragAcceptedByParent = false; - // The long press recognizer rejects the gesture after the AndroidView gets the pointer up event. - // This test makes sure that the Android view 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: AndroidView( - viewType: 'webview', - layoutDirection: TextDirection.ltr, - ), - ), - ), - ), - ); - - final TestGesture gesture = await tester.startGesture(const Offset(50.0, 50.0)); - await gesture.up(); - - expect(verticalDragAcceptedByParent, false); - expect( - viewsController.motionEvents[currentViewId + 1], - orderedEquals([ - const FakeAndroidMotionEvent( - AndroidViewController.kActionDown, [0], [Offset(50.0, 50.0)]), - const FakeAndroidMotionEvent( - AndroidViewController.kActionUp, [0], [Offset(50.0, 50.0)]), - ]), - ); - }); - - testWidgets('Android view rebuilt during gesture', (WidgetTester tester) async { - final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); - final FakeAndroidPlatformViewsController viewsController = FakeAndroidPlatformViewsController(); - viewsController.registerViewType('webview'); - await tester.pumpWidget( - Align( - alignment: Alignment.topLeft, + testWidgets('Android view can claim gesture after all pointers are up', (WidgetTester tester) async { + final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); + final FakePlatformViewsController viewsController = FakePlatformViewsController(TargetPlatform.android); + viewsController.registerViewType('webview'); + bool verticalDragAcceptedByParent = false; + // The long press recognizer rejects the gesture after the AndroidView gets the pointer up event. + // This test makes sure that the Android view 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, @@ -680,286 +612,159 @@ void main() { ), ), ), - ); + ), + ); - final TestGesture gesture = await tester.startGesture(const Offset(50.0, 50.0)); - await gesture.moveBy(const Offset(0.0, 100.0)); + final TestGesture gesture = await tester.startGesture(const Offset(50.0, 50.0)); + await gesture.up(); - await tester.pumpWidget( - Align( - alignment: Alignment.topLeft, + expect(verticalDragAcceptedByParent, false); + expect( + viewsController.motionEvents[currentViewId + 1], + orderedEquals([ + const FakeMotionEvent(AndroidViewController.kActionDown, [0], [Offset(50.0, 50.0)]), + const FakeMotionEvent(AndroidViewController.kActionUp, [0], [Offset(50.0, 50.0)]), + ]), + ); + }); + + testWidgets('Android view rebuilt during gesture', (WidgetTester tester) async { + final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); + final FakePlatformViewsController viewsController = FakePlatformViewsController(TargetPlatform.android); + viewsController.registerViewType('webview'); + await tester.pumpWidget( + Align( + alignment: Alignment.topLeft, + child: SizedBox( + width: 200.0, + height: 100.0, + child: AndroidView( + viewType: 'webview', + layoutDirection: TextDirection.ltr, + ), + ), + ), + ); + + 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: AndroidView( + viewType: 'webview', + layoutDirection: TextDirection.ltr, + ), + ), + ), + ); + + await gesture.up(); + + expect( + viewsController.motionEvents[currentViewId + 1], + orderedEquals([ + const FakeMotionEvent(AndroidViewController.kActionDown, [0], [Offset(50.0, 50.0)]), + const FakeMotionEvent(AndroidViewController.kActionMove, [0], [Offset(50.0, 150.0)]), + const FakeMotionEvent(AndroidViewController.kActionUp, [0], [Offset(50.0, 150.0)]), + ]), + ); + }); + + testWidgets('Android view with eager gesture recognizer', (WidgetTester tester) async { + final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); + final FakePlatformViewsController viewsController = FakePlatformViewsController(TargetPlatform.android); + viewsController.registerViewType('webview'); + await tester.pumpWidget( + Align( + alignment: Alignment.topLeft, + child: GestureDetector( + onVerticalDragStart: (DragStartDetails d) {}, child: SizedBox( width: 200.0, height: 100.0, child: AndroidView( viewType: 'webview', + gestureRecognizers: >[ + Factory( + () => EagerGestureRecognizer(), + ), + ].toSet(), layoutDirection: TextDirection.ltr, ), ), ), - ); + ), + ); - await gesture.up(); + await tester.startGesture(const Offset(50.0, 50.0)); - expect( - viewsController.motionEvents[currentViewId + 1], - orderedEquals([ - const FakeAndroidMotionEvent( - AndroidViewController.kActionDown, [0], [Offset(50.0, 50.0)]), - const FakeAndroidMotionEvent( - AndroidViewController.kActionMove, [0], [Offset(50.0, 150.0)]), - const FakeAndroidMotionEvent( - AndroidViewController.kActionUp, [0], [Offset(50.0, 150.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 Android view). Here we assert that with the eager recognizer in the gesture team the + // pointer down event is immediately dispatched. + expect( + viewsController.motionEvents[currentViewId + 1], + orderedEquals([ + const FakeMotionEvent(AndroidViewController.kActionDown, [0], [Offset(50.0, 50.0)]), + ]), + ); + }); - testWidgets('Android view with eager gesture recognizer', (WidgetTester tester) async { - final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); - final FakeAndroidPlatformViewsController viewsController = FakeAndroidPlatformViewsController(); - viewsController.registerViewType('webview'); - await tester.pumpWidget( - Align( - alignment: Alignment.topLeft, - child: GestureDetector( - onVerticalDragStart: (DragStartDetails d) {}, - child: SizedBox( - width: 200.0, - height: 100.0, - child: AndroidView( - viewType: 'webview', - gestureRecognizers: >[ - Factory( - () => EagerGestureRecognizer(), - ), - ].toSet(), - layoutDirection: TextDirection.ltr, - ), - ), - ), + testWidgets('RenderAndroidView reconstructed with same gestureRecognizers', (WidgetTester tester) async { + final FakePlatformViewsController viewsController = FakePlatformViewsController(TargetPlatform.android); + viewsController.registerViewType('webview'); + + final AndroidView androidView = AndroidView( + viewType: 'webview', + gestureRecognizers: >[ + Factory( + () => EagerGestureRecognizer(), ), - ); + ].toSet(), + layoutDirection: TextDirection.ltr, + ); - await tester.startGesture(const Offset(50.0, 50.0)); + await tester.pumpWidget(androidView); + await tester.pumpWidget(const SizedBox.shrink()); + await tester.pumpWidget(androidView); + }); - // 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 Android view). Here we assert that with the eager recognizer in the gesture team the - // pointer down event is immediately dispatched. - expect( - viewsController.motionEvents[currentViewId + 1], - orderedEquals([ - const FakeAndroidMotionEvent( - AndroidViewController.kActionDown, [0], [Offset(50.0, 50.0)]), - ]), - ); - }); + testWidgets('AndroidView rebuilt with same gestureRecognizers', (WidgetTester tester) async { + final FakePlatformViewsController viewsController = FakePlatformViewsController(TargetPlatform.android); + viewsController.registerViewType('webview'); - testWidgets('RenderAndroidView reconstructed with same gestureRecognizers', ( - WidgetTester tester) async { - final FakeAndroidPlatformViewsController viewsController = FakeAndroidPlatformViewsController(); - viewsController.registerViewType('webview'); - - final AndroidView androidView = AndroidView( - viewType: 'webview', - gestureRecognizers: >[ - Factory( - () => EagerGestureRecognizer(), - ), - ].toSet(), - layoutDirection: TextDirection.ltr, - ); - - await tester.pumpWidget(androidView); - await tester.pumpWidget(const SizedBox.shrink()); - await tester.pumpWidget(androidView); - }); - - testWidgets('AndroidView rebuilt with same gestureRecognizers', (WidgetTester tester) async { - final FakeAndroidPlatformViewsController viewsController = FakeAndroidPlatformViewsController(); - viewsController.registerViewType('webview'); - - int factoryInvocationCount = 0; - final ValueGetter constructRecognizer = () { + int factoryInvocationCount = 0; + final ValueGetter constructRecognizer = () { factoryInvocationCount += 1; return EagerGestureRecognizer(); }; - await tester.pumpWidget( - AndroidView( - viewType: 'webview', - gestureRecognizers: >[ - Factory(constructRecognizer), - ].toSet(), - layoutDirection: TextDirection.ltr, - ), - ); + await tester.pumpWidget( + AndroidView( + viewType: 'webview', + gestureRecognizers: >[ + Factory(constructRecognizer), + ].toSet(), + layoutDirection: TextDirection.ltr, + ), + ); - await tester.pumpWidget( - AndroidView( - viewType: 'webview', - hitTestBehavior: PlatformViewHitTestBehavior.translucent, - gestureRecognizers: >[ - Factory(constructRecognizer), - ].toSet(), - layoutDirection: TextDirection.ltr, - ), - ); - - expect(factoryInvocationCount, 1); - }); - }); - - group('UiKitView', () { - testWidgets('Create UIView', (WidgetTester tester) async { - final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); - final FakeIosPlatformViewsController viewsController = FakeIosPlatformViewsController(); - viewsController.registerViewType('webview'); - - await tester.pumpWidget( - Center( - child: SizedBox( - width: 200.0, - height: 100.0, - child: UiKitView(viewType: 'webview', layoutDirection: TextDirection.ltr), - ), - ), - ); - - expect( - viewsController.views, - unorderedEquals([ - FakeUiKitView(currentViewId + 1, 'webview') - ]), - ); - }); - - testWidgets('Change UIView view type', (WidgetTester tester) async { - final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); - final FakeIosPlatformViewsController viewsController = FakeIosPlatformViewsController(); - viewsController.registerViewType('webview'); - viewsController.registerViewType('maps'); - await tester.pumpWidget( - Center( - child: SizedBox( - width: 200.0, - height: 100.0, - child: UiKitView(viewType: 'webview', layoutDirection: TextDirection.ltr), - ), - ), - ); - - await tester.pumpWidget( - Center( - child: SizedBox( - width: 200.0, - height: 100.0, - child: UiKitView(viewType: 'maps', layoutDirection: TextDirection.ltr), - ), - ), - ); - - expect( - viewsController.views, - unorderedEquals([ - FakeUiKitView(currentViewId + 2, 'maps') - ]), - ); - }); - - testWidgets('Dispose UIView ', (WidgetTester tester) async { - final FakeIosPlatformViewsController viewsController = FakeIosPlatformViewsController(); - viewsController.registerViewType('webview'); - await tester.pumpWidget( - Center( - child: SizedBox( - width: 200.0, - height: 100.0, - child: UiKitView(viewType: 'webview', layoutDirection: TextDirection.ltr), - ), - ), - ); - - await tester.pumpWidget( - const Center( - child: SizedBox( - width: 200.0, - height: 100.0, - ), - ), - ); - - expect( - viewsController.views, - isEmpty, - ); - }); - - testWidgets('Dispose UIView before creation completed ', (WidgetTester tester) async { - final FakeIosPlatformViewsController viewsController = FakeIosPlatformViewsController(); - viewsController.registerViewType('webview'); - viewsController.creationDelay = Completer(); - await tester.pumpWidget( - Center( - child: SizedBox( - width: 200.0, - height: 100.0, - child: UiKitView(viewType: 'webview', layoutDirection: TextDirection.ltr), - ), - ), - ); - - await tester.pumpWidget( - const Center( - child: SizedBox( - width: 200.0, - height: 100.0, - ), - ), - ); - - viewsController.creationDelay.complete(); - - expect( - viewsController.views, - isEmpty, - ); - }); - - testWidgets('UIView survives widget tree change', (WidgetTester tester) async { - final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); - final FakeIosPlatformViewsController viewsController = FakeIosPlatformViewsController(); - viewsController.registerViewType('webview'); - final GlobalKey key = GlobalKey(); - await tester.pumpWidget( - Center( - child: SizedBox( - width: 200.0, - height: 100.0, - child: UiKitView(viewType: 'webview', layoutDirection: TextDirection.ltr, key: key), - ), - ), - ); - - await tester.pumpWidget( - Center( - child: Container( - child: SizedBox( - width: 200.0, - height: 100.0, - child: UiKitView(viewType: 'webview', layoutDirection: TextDirection.ltr, key: key), - ), - ), - ), - ); - - expect( - viewsController.views, - unorderedEquals([ - FakeUiKitView(currentViewId + 1, 'webview') - ]), - ); - }); + await tester.pumpWidget( + AndroidView( + viewType: 'webview', + hitTestBehavior: PlatformViewHitTestBehavior.translucent, + gestureRecognizers: >[ + Factory(constructRecognizer), + ].toSet(), + layoutDirection: TextDirection.ltr, + ), + ); + expect(factoryInvocationCount, 1); }); }