Unverified Commit 95ace11a authored by Maurice Parrish's avatar Maurice Parrish Committed by GitHub

Include initial offset when using PlatformViewSurface (#114103)

* add position to layout creation

* make position nullable

* fix tests

* test

* clear test size

* clear device pixel ratio

* add more documentaiton

* add comment about localToGlobal
parent acf01eb3
......@@ -192,7 +192,7 @@ class RenderAndroidView extends PlatformViewRenderBox {
markNeedsPaint();
}
// Sets the offset of the underlaying platform view on the platform side.
// Sets the offset of the underlying platform view on the platform side.
//
// This allows the Android native view to draw the a11y highlights in the same
// location on the screen as the platform view widget in the Flutter framework.
......
......@@ -779,7 +779,7 @@ abstract class AndroidViewController extends PlatformViewController {
///
/// If [_createRequiresSize] is true, `size` is non-nullable, and the call
/// should instead be deferred until the size is available.
Future<void> _sendCreateMessage({required covariant Size? size});
Future<void> _sendCreateMessage({required covariant Size? size, Offset? position});
/// Sends the message to resize the platform view to [size].
Future<Size> _sendResizeMessage(Size size);
......@@ -788,7 +788,7 @@ abstract class AndroidViewController extends PlatformViewController {
bool get awaitingCreation => _state == _AndroidViewState.waitingForSize;
@override
Future<void> create({Size? size}) async {
Future<void> create({Size? size, Offset? position}) async {
assert(_state != _AndroidViewState.disposed, 'trying to create a disposed Android view');
assert(_state == _AndroidViewState.waitingForSize, 'Android view is already sized. View id: $viewId');
......@@ -798,7 +798,7 @@ abstract class AndroidViewController extends PlatformViewController {
}
_state = _AndroidViewState.creating;
await _sendCreateMessage(size: size);
await _sendCreateMessage(size: size, position: position);
_state = _AndroidViewState.created;
for (final PlatformViewCreatedCallback callback in _platformViewCreatedCallbacks) {
......@@ -1011,7 +1011,7 @@ class SurfaceAndroidViewController extends AndroidViewController {
bool get _createRequiresSize => true;
@override
Future<bool> _sendCreateMessage({required Size size}) async {
Future<bool> _sendCreateMessage({required Size size, Offset? position}) async {
assert(!size.isEmpty, 'trying to create $TextureAndroidViewController without setting a valid size.');
final dynamic response = await _AndroidViewControllerInternals.sendCreateMessage(
......@@ -1022,6 +1022,7 @@ class SurfaceAndroidViewController extends AndroidViewController {
layoutDirection: _layoutDirection,
creationParams: _creationParams,
size: size,
position: position,
);
if (response is int) {
(_internals as _TextureAndroidViewControllerInternals).textureId = response;
......@@ -1076,13 +1077,14 @@ class ExpensiveAndroidViewController extends AndroidViewController {
bool get _createRequiresSize => false;
@override
Future<void> _sendCreateMessage({required Size? size}) async {
Future<void> _sendCreateMessage({required Size? size, Offset? position}) async {
await _AndroidViewControllerInternals.sendCreateMessage(
viewId: viewId,
viewType: _viewType,
hybrid: true,
layoutDirection: _layoutDirection,
creationParams: _creationParams,
position: position,
);
}
......@@ -1133,7 +1135,7 @@ class TextureAndroidViewController extends AndroidViewController {
bool get _createRequiresSize => true;
@override
Future<void> _sendCreateMessage({required Size size}) async {
Future<void> _sendCreateMessage({required Size size, Offset? position}) async {
assert(!size.isEmpty, 'trying to create $TextureAndroidViewController without setting a valid size.');
_internals.textureId = await _AndroidViewControllerInternals.sendCreateMessage(
......@@ -1143,6 +1145,7 @@ class TextureAndroidViewController extends AndroidViewController {
layoutDirection: _layoutDirection,
creationParams: _creationParams,
size: size,
position: position,
) as int;
}
......@@ -1190,7 +1193,8 @@ abstract class _AndroidViewControllerInternals {
required bool hybrid,
bool hybridFallback = false,
_CreationParams? creationParams,
Size? size}) {
Size? size,
Offset? position}) {
final Map<String, dynamic> args = <String, dynamic>{
'id': viewId,
'viewType': viewType,
......@@ -1199,6 +1203,8 @@ abstract class _AndroidViewControllerInternals {
if (size != null) 'width': size.width,
if (size != null) 'height': size.height,
if (hybridFallback == true) 'hybridFallback': hybridFallback,
if (position != null) 'left': position.dx,
if (position != null) 'top': position.dy,
};
if (creationParams != null) {
final ByteData paramsByteData = creationParams.codec.encodeMessage(creationParams.data)!;
......@@ -1449,7 +1455,11 @@ abstract class PlatformViewController {
/// [size] is the view's initial size in logical pixel.
/// [size] can be omitted if the concrete implementation doesn't require an initial size
/// to create the platform view.
Future<void> create({Size? size}) async {}
///
/// [position] is the view's initial position in logical pixels.
/// [position] can be omitted if the concrete implementation doesn't require
/// an initial position.
Future<void> create({Size? size, Offset? position}) async {}
/// Disposes the platform view.
///
......
......@@ -5,6 +5,7 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';
import 'basic.dart';
......@@ -879,9 +880,9 @@ class _PlatformViewLinkState extends State<PlatformViewLink> {
if (!_platformViewCreated) {
// Depending on the implementation, the first non-empty size can be used
// to size the platform view.
return _PlatformViewPlaceHolder(onLayout: (Size size) {
return _PlatformViewPlaceHolder(onLayout: (Size size, Offset position) {
if (controller.awaitingCreation && !size.isEmpty) {
controller.create(size: size);
controller.create(size: size, position: position);
}
});
}
......@@ -1188,7 +1189,7 @@ class _PlatformLayerBasedAndroidViewSurface extends PlatformViewSurface {
/// A callback used to notify the size of the platform view placeholder.
/// This size is the initial size of the platform view.
typedef _OnLayoutCallback = void Function(Size size);
typedef _OnLayoutCallback = void Function(Size size, Offset position);
/// A [RenderBox] that notifies its size to the owner after a layout.
class _PlatformViewPlaceholderBox extends RenderConstrainedBox {
......@@ -1204,7 +1205,10 @@ class _PlatformViewPlaceholderBox extends RenderConstrainedBox {
@override
void performLayout() {
super.performLayout();
onLayout(size);
// A call to `localToGlobal` requires waiting for a frame to render first.
SchedulerBinding.instance.addPostFrameCallback((_) {
onLayout(size, localToGlobal(Offset.zero));
});
}
}
......
......@@ -60,6 +60,8 @@ class FakeAndroidViewController implements AndroidViewController {
bool _createCalledSuccessfully = false;
Offset? createPosition;
final List<PlatformViewCreatedCallback> _createdCallbacks = <PlatformViewCreatedCallback>[];
/// Events that are dispatched.
......@@ -131,12 +133,13 @@ class FakeAndroidViewController implements AndroidViewController {
}
@override
Future<void> create({Size? size}) async {
Future<void> create({Size? size, Offset? position}) async {
assert(!_createCalledSuccessfully);
if (requiresSize && size != null) {
assert(!size.isEmpty);
}
_createCalledSuccessfully = size != null || !requiresSize;
_createCalledSuccessfully = size != null && position != null || !requiresSize;
createPosition = position;
}
@override
......@@ -214,6 +217,8 @@ class FakeAndroidPlatformViewsController {
final bool? hybrid = args['hybrid'] as bool?;
final bool? hybridFallback = args['hybridFallback'] as bool?;
final Uint8List? creationParams = args['params'] as Uint8List?;
final double? top = args['top'] as double?;
final double? left = args['left'] as double?;
if (_views.containsKey(id)) {
throw PlatformException(
......@@ -239,6 +244,7 @@ class FakeAndroidPlatformViewsController {
hybrid: hybrid,
hybridFallback: hybridFallback,
creationParams: creationParams,
position: left != null && top != null ? Offset(left, top) : null,
);
// Return a hybrid result (null rather than a texture ID) if:
final bool hybridResult =
......@@ -538,7 +544,7 @@ class FakeHtmlPlatformViewsController {
@immutable
class FakeAndroidPlatformView {
const FakeAndroidPlatformView(this.id, this.type, this.size, this.layoutDirection,
{this.hybrid, this.hybridFallback, this.creationParams});
{this.hybrid, this.hybridFallback, this.creationParams, this.position});
final int id;
final String type;
......@@ -547,6 +553,7 @@ class FakeAndroidPlatformView {
final int layoutDirection;
final bool? hybrid;
final bool? hybridFallback;
final Offset? position;
FakeAndroidPlatformView copyWith({Size? size, int? layoutDirection}) => FakeAndroidPlatformView(
id,
......@@ -556,6 +563,7 @@ class FakeAndroidPlatformView {
hybrid: hybrid,
hybridFallback: hybridFallback,
creationParams: creationParams,
position: position,
);
@override
......@@ -570,7 +578,8 @@ class FakeAndroidPlatformView {
&& other.size == size
&& other.hybrid == hybrid
&& other.hybridFallback == hybridFallback
&& other.layoutDirection == layoutDirection;
&& other.layoutDirection == layoutDirection
&& other.position == position;
}
@override
......@@ -582,13 +591,14 @@ class FakeAndroidPlatformView {
layoutDirection,
hybrid,
hybridFallback,
position,
);
@override
String toString() {
return 'FakeAndroidPlatformView(id: $id, type: $type, size: $size, '
'layoutDirection: $layoutDirection, hybrid: $hybrid, '
'hybridFallback: $hybridFallback, creationParams: $creationParams)';
'hybridFallback: $hybridFallback, creationParams: $creationParams, position: $position)';
}
}
......
......@@ -2599,6 +2599,51 @@ void main() {
},
);
testWidgets('PlatformViewLink includes offset in create call when using texture layer', (WidgetTester tester) async {
late FakeAndroidViewController controller;
final PlatformViewLink platformViewLink = PlatformViewLink(
viewType: 'webview',
onCreatePlatformView: (PlatformViewCreationParams params) {
controller = FakeAndroidViewController(params.id, requiresSize: true);
controller.create();
// This test should be simulating one of the texture-based display
// modes, where `create` is a no-op when not provided a size, and
// creation is triggered via a later call to setSize, or to `create`
// with a size.
expect(controller.awaitingCreation, true);
return controller;
},
surfaceFactory: (BuildContext context, PlatformViewController controller) {
return PlatformViewSurface(
gestureRecognizers: const <Factory<OneSequenceGestureRecognizer>>{},
controller: controller,
hitTestBehavior: PlatformViewHitTestBehavior.opaque,
);
},
);
TestWidgetsFlutterBinding.instance.window.physicalSizeTestValue = const Size(400, 200);
TestWidgetsFlutterBinding.instance.window.devicePixelRatioTestValue = 1.0;
await tester.pumpWidget(
Container(
constraints: const BoxConstraints.expand(),
alignment: Alignment.center,
child: SizedBox(
width: 100,
height: 50,
child: platformViewLink,
),
)
);
expect(controller.createPosition, const Offset(150, 75));
TestWidgetsFlutterBinding.instance.window.clearPhysicalSizeTestValue();
TestWidgetsFlutterBinding.instance.window.clearDevicePixelRatioTestValue();
});
testWidgets(
'PlatformViewLink does not double-call create for Android Hybrid Composition',
(WidgetTester tester) async {
......@@ -2616,7 +2661,7 @@ void main() {
controller = FakeAndroidViewController(params.id);
controller.create();
// This test should be simulating Hybrid Composition mode, where
// `create` takes effect immidately.
// `create` takes effect immediately.
expect(controller.awaitingCreation, false);
return controller;
},
......
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