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
This commit is contained in:
parent
acf01eb3e3
commit
95ace11afa
@ -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;
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user