Unverified Commit 1c2c9421 authored by Zachary Anderson's avatar Zachary Anderson Committed by GitHub

Revert "Always use texture layer when displaying an Android view (#100091)" (#100222)

This reverts commit 509ddfda.
parent 509ddfda
......@@ -52,7 +52,7 @@ Set<Type> _factoriesTypeSet<T>(Set<Factory<T>> factories) {
/// A render object for an Android view.
///
/// Requires Android API level 23 or greater.
/// Requires Android API level 20 or greater.
///
/// [RenderAndroidView] is responsible for sizing, displaying and passing touch events to an
/// Android [View](https://developer.android.com/reference/android/view/View).
......@@ -74,7 +74,7 @@ Set<Type> _factoriesTypeSet<T>(Set<Factory<T>> factories) {
///
/// * [AndroidView] which is a widget that is used to show an Android view.
/// * [PlatformViewsService] which is a service for controlling platform views.
class RenderAndroidView extends PlatformViewRenderBox {
class RenderAndroidView extends RenderBox with _PlatformViewGestureMixin {
/// Creates a render object for an Android view.
RenderAndroidView({
required AndroidViewController viewController,
......@@ -86,8 +86,7 @@ class RenderAndroidView extends PlatformViewRenderBox {
assert(gestureRecognizers != null),
assert(clipBehavior != null),
_viewController = viewController,
_clipBehavior = clipBehavior,
super(controller: viewController, hitTestBehavior: hitTestBehavior, gestureRecognizers: gestureRecognizers) {
_clipBehavior = clipBehavior {
_viewController.pointTransformer = (Offset offset) => globalToLocal(offset);
updateGestureRecognizers(gestureRecognizers);
_viewController.addOnPlatformViewCreatedListener(_onPlatformViewCreated);
......@@ -102,22 +101,18 @@ class RenderAndroidView extends PlatformViewRenderBox {
bool _isDisposed = false;
/// The Android view controller for the Android view associated with this render object.
@override
AndroidViewController get controller => _viewController;
AndroidViewController get viewController => _viewController;
AndroidViewController _viewController;
/// Sets a new Android view controller.
@override
set controller(AndroidViewController controller) {
///
/// `viewController` must not be null.
set viewController(AndroidViewController viewController) {
assert(_viewController != null);
assert(controller != null);
if (_viewController == controller)
assert(viewController != null);
if (_viewController == viewController)
return;
_viewController.removeOnPlatformViewCreatedListener(_onPlatformViewCreated);
super.controller = controller;
_viewController = controller;
_viewController.pointTransformer = (Offset offset) => globalToLocal(offset);
_viewController = viewController;
_sizePlatformView();
if (_viewController.isCreated) {
markNeedsSemanticsUpdate();
......@@ -143,6 +138,26 @@ class RenderAndroidView extends PlatformViewRenderBox {
markNeedsSemanticsUpdate();
}
/// {@template flutter.rendering.RenderAndroidView.updateGestureRecognizers}
/// Updates which gestures should be forwarded to the platform view.
///
/// Gesture recognizers created by factories in this set participate in the gesture arena for each
/// pointer that was put down on the render box. If any of the recognizers on this list wins the
/// gesture arena, the entire pointer event sequence starting from the pointer down event
/// will be dispatched to the Android view.
///
/// The `gestureRecognizers` property must not contain more than one factory with the same [Factory.type].
///
/// Setting a new set of gesture recognizer factories with the same [Factory.type]s as the current
/// set has no effect, because the factories' constructors would have already been called with the previous set.
/// {@endtemplate}
///
/// Any active gesture arena the Android view participates in is rejected when the
/// set of gesture recognizers is changed.
void updateGestureRecognizers(Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers) {
_updateGestureRecognizersWithCallBack(gestureRecognizers, _viewController.dispatchPointerEvent);
}
@override
bool get sizedByParent => true;
......@@ -167,8 +182,9 @@ class RenderAndroidView extends PlatformViewRenderBox {
// Android virtual displays cannot have a zero size.
// Trying to size it to 0 crashes the app, which was happening when starting the app
// with a locked screen (see: https://github.com/flutter/flutter/issues/20456).
if (_state == _PlatformViewState.resizing || size.isEmpty)
if (_state == _PlatformViewState.resizing || size.isEmpty) {
return;
}
_state = _PlatformViewState.resizing;
markNeedsPaint();
......@@ -196,8 +212,7 @@ class RenderAndroidView extends PlatformViewRenderBox {
void _setOffset() {
SchedulerBinding.instance.addPostFrameCallback((_) async {
if (!_isDisposed) {
if (attached)
await _viewController.setOffset(localToGlobal(Offset.zero));
await _viewController.setOffset(localToGlobal(Offset.zero));
// Schedule a new post frame callback.
_setOffset();
}
......@@ -206,7 +221,7 @@ class RenderAndroidView extends PlatformViewRenderBox {
@override
void paint(PaintingContext context, Offset offset) {
if (_viewController.textureId == null || _currentTextureSize == null)
if (_viewController.textureId == null)
return;
// As resizing the Android view happens asynchronously we don't know exactly when is a
......@@ -249,15 +264,14 @@ class RenderAndroidView extends PlatformViewRenderBox {
context.addLayer(TextureLayer(
rect: offset & _currentTextureSize!,
textureId: _viewController.textureId!,
textureId: viewController.textureId!,
));
}
@override
void describeSemanticsConfiguration(SemanticsConfiguration config) {
// Don't call the super implementation since `platformViewId` should
// be set only when the platform view is created, but the concept of
// a "created" platform view belongs to this subclass.
void describeSemanticsConfiguration (SemanticsConfiguration config) {
super.describeSemanticsConfiguration(config);
config.isSemanticBoundary = true;
if (_viewController.isCreated) {
......@@ -325,7 +339,7 @@ class RenderUiKitView extends RenderBox {
// any newly arriving events there's nothing we need to invalidate.
PlatformViewHitTestBehavior hitTestBehavior;
/// {@macro flutter.rendering.PlatformViewRenderBox.updateGestureRecognizers}
/// {@macro flutter.rendering.RenderAndroidView.updateGestureRecognizers}
void updateGestureRecognizers(Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers) {
assert(gestureRecognizers != null);
assert(
......@@ -639,11 +653,11 @@ class PlatformViewRenderBox extends RenderBox with _PlatformViewGestureMixin {
PlatformViewController get controller => _controller;
PlatformViewController _controller;
/// This value must not be null, and setting it to a new value will result in a repaint.
set controller(covariant PlatformViewController controller) {
set controller(PlatformViewController controller) {
assert(controller != null);
assert(controller.viewId != null && controller.viewId > -1);
if (_controller == controller) {
if ( _controller == controller) {
return;
}
final bool needsSemanticsUpdate = _controller.viewId != controller.viewId;
......@@ -654,19 +668,7 @@ class PlatformViewRenderBox extends RenderBox with _PlatformViewGestureMixin {
}
}
/// {@template flutter.rendering.PlatformViewRenderBox.updateGestureRecognizers}
/// Updates which gestures should be forwarded to the platform view.
///
/// Gesture recognizers created by factories in this set participate in the gesture arena for each
/// pointer that was put down on the render box. If any of the recognizers on this list wins the
/// gesture arena, the entire pointer event sequence starting from the pointer down event
/// will be dispatched to the Android view.
///
/// The `gestureRecognizers` property must not contain more than one factory with the same [Factory.type].
///
/// Setting a new set of gesture recognizer factories with the same [Factory.type]s as the current
/// set has no effect, because the factories' constructors would have already been called with the previous set.
/// {@endtemplate}
/// {@macro flutter.rendering.RenderAndroidView.updateGestureRecognizers}
///
/// Any active gesture arena the `PlatformView` participates in is rejected when the
/// set of gesture recognizers is changed.
......@@ -698,7 +700,7 @@ class PlatformViewRenderBox extends RenderBox with _PlatformViewGestureMixin {
}
@override
void describeSemanticsConfiguration(SemanticsConfiguration config) {
void describeSemanticsConfiguration (SemanticsConfiguration config) {
super.describeSemanticsConfiguration(config);
assert(_controller.viewId != null);
config.isSemanticBoundary = true;
......
......@@ -15,7 +15,7 @@ import 'framework.dart';
/// Embeds an Android view in the Widget hierarchy.
///
/// Requires Android API level 23 or greater.
/// Requires Android API level 20 or greater.
///
/// Embedding Android views is an expensive operation and should be avoided when a Flutter
/// equivalent is possible.
......@@ -681,7 +681,7 @@ class _AndroidPlatformView extends LeafRenderObjectWidget {
@override
void updateRenderObject(BuildContext context, RenderAndroidView renderObject) {
renderObject.controller = controller;
renderObject.viewController = controller;
renderObject.hitTestBehavior = hitTestBehavior;
renderObject.updateGestureRecognizers(gestureRecognizers);
renderObject.clipBehavior = clipBehavior;
......@@ -842,11 +842,15 @@ class PlatformViewLink extends StatefulWidget {
class _PlatformViewLinkState extends State<PlatformViewLink> {
int? _id;
PlatformViewController? _controller;
bool _platformViewCreated = false;
Widget? _surface;
FocusNode? _focusNode;
@override
Widget build(BuildContext context) {
if (!_platformViewCreated) {
return const SizedBox.expand();
}
_surface ??= widget._surfaceFactory(context, _controller!);
return Focus(
focusNode: _focusNode,
......@@ -871,6 +875,9 @@ class _PlatformViewLinkState extends State<PlatformViewLink> {
// The _surface has to be recreated as its controller is disposed.
// Setting _surface to null will trigger its creation in build().
_surface = null;
// We are about to create a new platform view.
_platformViewCreated = false;
_initialize();
}
}
......@@ -881,12 +888,16 @@ class _PlatformViewLinkState extends State<PlatformViewLink> {
PlatformViewCreationParams._(
id: _id!,
viewType: widget.viewType,
onPlatformViewCreated: (_) {},
onPlatformViewCreated: _onPlatformViewCreated,
onFocusChanged: _handlePlatformFocusChanged,
),
);
}
void _onPlatformViewCreated(int id) {
setState(() { _platformViewCreated = true; });
}
void _handleFrameworkFocusChanged(bool isFocused) {
if (!isFocused) {
_controller?.clearFocus();
......@@ -1009,18 +1020,18 @@ class PlatformViewSurface extends LeafRenderObjectWidget {
/// Integrates an Android view with Flutter's compositor, touch, and semantics subsystems.
///
/// The compositor integration is done by adding a [TextureLayer] to the layer tree.
/// The compositor integration is done by adding a [PlatformViewLayer] to the layer tree. [PlatformViewLayer]
/// isn't supported on all platforms. Custom Flutter embedders can support
/// [PlatformViewLayer]s by implementing a SystemCompositor.
///
/// The parent of this object must provide bounded layout constraints.
/// The widget fills all available space, the parent of this object must provide bounded layout
/// constraints.
///
/// If the associated platform view is not created, the [AndroidViewSurface] does not paint any contents.
///
/// When possible, you may want to use [AndroidView] directly, since it requires less boilerplate code
/// than [AndroidViewSurface], and there's no difference in performance, or other trade-off(s).
///
/// See also:
///
/// * [AndroidView] which embeds an Android platform view in the widget hierarchy.
/// * [AndroidView] which embeds an Android platform view in the widget hierarchy using a [TextureLayer].
/// * [UiKitView] which embeds an iOS platform view in the widget hierarchy.
class AndroidViewSurface extends PlatformViewSurface {
/// Construct an `AndroidPlatformViewSurface`.
......@@ -1041,22 +1052,12 @@ class AndroidViewSurface extends PlatformViewSurface {
@override
RenderObject createRenderObject(BuildContext context) {
final AndroidViewController viewController = controller as AndroidViewController;
final RenderAndroidView renderObject = RenderAndroidView(
viewController: viewController,
gestureRecognizers: gestureRecognizers,
hitTestBehavior: hitTestBehavior,
);
viewController.pointTransformer =
(Offset position) => renderObject.globalToLocal(position);
return renderObject;
}
final PlatformViewRenderBox renderBox =
super.createRenderObject(context) as PlatformViewRenderBox;
@override
void updateRenderObject(BuildContext context, RenderAndroidView renderObject) {
renderObject
..controller = controller as AndroidViewController
..hitTestBehavior = hitTestBehavior
..updateGestureRecognizers(gestureRecognizers);
(controller as AndroidViewController).pointTransformer =
(Offset position) => renderBox.globalToLocal(position);
return renderBox;
}
}
......@@ -151,38 +151,6 @@ void main() {
// Passes if no crashes.
});
test('created callback is reset when controller is changed', () {
final FakeAndroidPlatformViewsController viewsController = FakeAndroidPlatformViewsController();
viewsController.registerViewType('webview');
final AndroidViewController firstController = PlatformViewsService.initAndroidView(
id: 0,
viewType: 'webview',
layoutDirection: TextDirection.rtl,
);
final RenderAndroidView renderBox = RenderAndroidView(
viewController: firstController,
hitTestBehavior: PlatformViewHitTestBehavior.opaque,
gestureRecognizers: <Factory<OneSequenceGestureRecognizer>>{},
);
layout(renderBox);
pumpFrame(phase: EnginePhase.flushSemantics);
expect(firstController.createdCallbacks, isNotEmpty);
expect(firstController.createdCallbacks.length, 1);
final AndroidViewController secondController = PlatformViewsService.initAndroidView(
id: 0,
viewType: 'webview',
layoutDirection: TextDirection.rtl,
);
// Reset controller.
renderBox.controller = secondController;
expect(firstController.createdCallbacks, isEmpty);
expect(secondController.createdCallbacks, isNotEmpty);
expect(secondController.createdCallbacks.length, 1);
});
test('render object changed its visual appearance after texture is created', () {
FakeAsync().run((FakeAsync async) {
final AndroidViewController viewController =
......
......@@ -84,22 +84,23 @@ class FakeAndroidViewController implements AndroidViewController {
@override
Future<Size> setSize(Size size) {
return Future<Size>.value(size);
throw UnimplementedError();
}
@override
Future<void> setOffset(Offset off) async {}
Future<void> setOffset(Offset off) {
throw UnimplementedError();
}
@override
int get textureId => 0;
int get textureId => throw UnimplementedError();
@override
bool get isCreated => created;
bool get isCreated => throw UnimplementedError();
@override
void addOnPlatformViewCreatedListener(PlatformViewCreatedCallback listener) {
created = true;
}
void addOnPlatformViewCreatedListener(PlatformViewCreatedCallback listener) =>
throw UnimplementedError();
@override
void removeOnPlatformViewCreatedListener(PlatformViewCreatedCallback listener) {
......@@ -117,10 +118,9 @@ class FakeAndroidViewController implements AndroidViewController {
}
@override
Future<void> create() async {}
@override
List<PlatformViewCreatedCallback> get createdCallbacks => <PlatformViewCreatedCallback>[];
Future<void> create() async {
created = true;
}
}
class FakeAndroidPlatformViewsController {
......@@ -143,6 +143,8 @@ class FakeAndroidPlatformViewsController {
int? lastClearedFocusViewId;
bool synchronizeToNativeViewHierarchy = true;
Map<int, Offset> offsets = <int, Offset>{};
void registerViewType(String viewType) {
......@@ -172,6 +174,8 @@ class FakeAndroidPlatformViewsController {
return _clearFocus(call);
case 'offset':
return _offset(call);
case 'synchronizeToNativeViewHierarchy':
return _synchronizeToNativeViewHierarchy(call);
}
return Future<dynamic>.sync(() => null);
}
......@@ -314,6 +318,11 @@ class FakeAndroidPlatformViewsController {
lastClearedFocusViewId = id;
return Future<dynamic>.sync(() => null);
}
Future<dynamic> _synchronizeToNativeViewHierarchy(MethodCall call) {
synchronizeToNativeViewHierarchy = call.arguments as bool;
return Future<dynamic>.sync(() => null);
}
}
class FakeIosPlatformViewsController {
......
......@@ -18,7 +18,7 @@ void main() {
});
test('create Android view of unregistered type', () async {
expectLater(
expect(
() {
return PlatformViewsService.initAndroidView(
id: 0,
......@@ -29,25 +29,16 @@ void main() {
throwsA(isA<PlatformException>()),
);
try {
await PlatformViewsService.initSurfaceAndroidView(
id: 0,
viewType: 'web',
layoutDirection: TextDirection.ltr,
).create();
} catch (e) {
expect(false, isTrue, reason: 'did not expected any exception, but instead got `$e`');
}
try {
await PlatformViewsService.initAndroidView(
id: 0,
viewType: 'web',
layoutDirection: TextDirection.ltr,
).create();
} catch (e) {
expect(false, isTrue, reason: 'did not expected any exception, but instead got `$e`');
}
expect(
() {
return PlatformViewsService.initSurfaceAndroidView(
id: 0,
viewType: 'web',
layoutDirection: TextDirection.ltr,
).create();
},
throwsA(isA<PlatformException>()),
);
});
test('create Android views', () async {
......@@ -56,13 +47,13 @@ void main() {
.setSize(const Size(100.0, 100.0));
await PlatformViewsService.initAndroidView( id: 1, viewType: 'webview', layoutDirection: TextDirection.rtl)
.setSize(const Size(200.0, 300.0));
// This platform view isn't created until the size is set.
await PlatformViewsService.initSurfaceAndroidView(id: 2, viewType: 'webview', layoutDirection: TextDirection.rtl).create();
expect(
viewsController.views,
unorderedEquals(<FakeAndroidPlatformView>[
const FakeAndroidPlatformView(0, 'webview', Size(100.0, 100.0), AndroidViewController.kAndroidLayoutDirectionLtr, null),
const FakeAndroidPlatformView(1, 'webview', Size(200.0, 300.0), AndroidViewController.kAndroidLayoutDirectionRtl, null),
const FakeAndroidPlatformView(2, 'webview', null, AndroidViewController.kAndroidLayoutDirectionRtl, true),
]),
);
});
......@@ -74,7 +65,7 @@ void main() {
viewType: 'webview',
layoutDirection: TextDirection.ltr,
).setSize(const Size(100.0, 100.0));
expectLater(
expect(
() => PlatformViewsService.initAndroidView(
id: 0,
viewType: 'web',
......@@ -82,6 +73,20 @@ void main() {
).setSize(const Size(100.0, 100.0)),
throwsA(isA<PlatformException>()),
);
await PlatformViewsService.initSurfaceAndroidView(
id: 1,
viewType: 'webview',
layoutDirection: TextDirection.ltr,
).create();
expect(
() => PlatformViewsService.initSurfaceAndroidView(
id: 1,
viewType: 'web',
layoutDirection: TextDirection.ltr,
).create(),
throwsA(isA<PlatformException>()),
);
});
test('dispose Android view', () async {
......@@ -235,6 +240,11 @@ void main() {
await viewController.setOffset(const Offset(10, 20));
expect(viewsController.offsets, equals(<int, Offset>{}));
});
test('synchronizeToNativeViewHierarchy', () async {
await PlatformViewsService.synchronizeToNativeViewHierarchy(false);
expect(viewsController.synchronizeToNativeViewHierarchy, false);
});
});
group('iOS', () {
......
......@@ -2312,6 +2312,43 @@ void main() {
expect(factoryInvocationCount, 1);
});
testWidgets(
'PlatformViewLink Widget init, should create a SizedBox widget before onPlatformViewCreated and a PlatformViewSurface after',
(WidgetTester tester) async {
final int currentViewId = platformViewsRegistry.getNextPlatformViewId();
late int createdPlatformViewId;
late PlatformViewCreatedCallback onPlatformViewCreatedCallBack;
final PlatformViewLink platformViewLink = PlatformViewLink(
viewType: 'webview',
onCreatePlatformView: (PlatformViewCreationParams params) {
onPlatformViewCreatedCallBack = params.onPlatformViewCreated;
createdPlatformViewId = params.id;
return FakePlatformViewController(params.id);
},
surfaceFactory: (BuildContext context, PlatformViewController controller) {
return PlatformViewSurface(
gestureRecognizers: const <Factory<OneSequenceGestureRecognizer>>{},
controller: controller,
hitTestBehavior: PlatformViewHitTestBehavior.opaque,
);
},
);
await tester.pumpWidget(platformViewLink);
expect(() => tester.allWidgets.whereType<SizedBox>().first, returnsNormally);
onPlatformViewCreatedCallBack(createdPlatformViewId);
await tester.pump();
expect(() => tester.allWidgets.whereType<PlatformViewSurface>().first, returnsNormally);
expect(createdPlatformViewId, currentViewId + 1);
},
);
testWidgets('PlatformViewLink Widget dispose', (WidgetTester tester) async {
late FakePlatformViewController disposedController;
final PlatformViewLink platformViewLink = PlatformViewLink(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment