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();
|
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
|
// 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.
|
// 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
|
/// If [_createRequiresSize] is true, `size` is non-nullable, and the call
|
||||||
/// should instead be deferred until the size is available.
|
/// 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].
|
/// Sends the message to resize the platform view to [size].
|
||||||
Future<Size> _sendResizeMessage(Size size);
|
Future<Size> _sendResizeMessage(Size size);
|
||||||
@ -788,7 +788,7 @@ abstract class AndroidViewController extends PlatformViewController {
|
|||||||
bool get awaitingCreation => _state == _AndroidViewState.waitingForSize;
|
bool get awaitingCreation => _state == _AndroidViewState.waitingForSize;
|
||||||
|
|
||||||
@override
|
@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.disposed, 'trying to create a disposed Android view');
|
||||||
assert(_state == _AndroidViewState.waitingForSize, 'Android view is already sized. View id: $viewId');
|
assert(_state == _AndroidViewState.waitingForSize, 'Android view is already sized. View id: $viewId');
|
||||||
|
|
||||||
@ -798,7 +798,7 @@ abstract class AndroidViewController extends PlatformViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_state = _AndroidViewState.creating;
|
_state = _AndroidViewState.creating;
|
||||||
await _sendCreateMessage(size: size);
|
await _sendCreateMessage(size: size, position: position);
|
||||||
_state = _AndroidViewState.created;
|
_state = _AndroidViewState.created;
|
||||||
|
|
||||||
for (final PlatformViewCreatedCallback callback in _platformViewCreatedCallbacks) {
|
for (final PlatformViewCreatedCallback callback in _platformViewCreatedCallbacks) {
|
||||||
@ -1011,7 +1011,7 @@ class SurfaceAndroidViewController extends AndroidViewController {
|
|||||||
bool get _createRequiresSize => true;
|
bool get _createRequiresSize => true;
|
||||||
|
|
||||||
@override
|
@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.');
|
assert(!size.isEmpty, 'trying to create $TextureAndroidViewController without setting a valid size.');
|
||||||
|
|
||||||
final dynamic response = await _AndroidViewControllerInternals.sendCreateMessage(
|
final dynamic response = await _AndroidViewControllerInternals.sendCreateMessage(
|
||||||
@ -1022,6 +1022,7 @@ class SurfaceAndroidViewController extends AndroidViewController {
|
|||||||
layoutDirection: _layoutDirection,
|
layoutDirection: _layoutDirection,
|
||||||
creationParams: _creationParams,
|
creationParams: _creationParams,
|
||||||
size: size,
|
size: size,
|
||||||
|
position: position,
|
||||||
);
|
);
|
||||||
if (response is int) {
|
if (response is int) {
|
||||||
(_internals as _TextureAndroidViewControllerInternals).textureId = response;
|
(_internals as _TextureAndroidViewControllerInternals).textureId = response;
|
||||||
@ -1076,13 +1077,14 @@ class ExpensiveAndroidViewController extends AndroidViewController {
|
|||||||
bool get _createRequiresSize => false;
|
bool get _createRequiresSize => false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> _sendCreateMessage({required Size? size}) async {
|
Future<void> _sendCreateMessage({required Size? size, Offset? position}) async {
|
||||||
await _AndroidViewControllerInternals.sendCreateMessage(
|
await _AndroidViewControllerInternals.sendCreateMessage(
|
||||||
viewId: viewId,
|
viewId: viewId,
|
||||||
viewType: _viewType,
|
viewType: _viewType,
|
||||||
hybrid: true,
|
hybrid: true,
|
||||||
layoutDirection: _layoutDirection,
|
layoutDirection: _layoutDirection,
|
||||||
creationParams: _creationParams,
|
creationParams: _creationParams,
|
||||||
|
position: position,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1133,7 +1135,7 @@ class TextureAndroidViewController extends AndroidViewController {
|
|||||||
bool get _createRequiresSize => true;
|
bool get _createRequiresSize => true;
|
||||||
|
|
||||||
@override
|
@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.');
|
assert(!size.isEmpty, 'trying to create $TextureAndroidViewController without setting a valid size.');
|
||||||
|
|
||||||
_internals.textureId = await _AndroidViewControllerInternals.sendCreateMessage(
|
_internals.textureId = await _AndroidViewControllerInternals.sendCreateMessage(
|
||||||
@ -1143,6 +1145,7 @@ class TextureAndroidViewController extends AndroidViewController {
|
|||||||
layoutDirection: _layoutDirection,
|
layoutDirection: _layoutDirection,
|
||||||
creationParams: _creationParams,
|
creationParams: _creationParams,
|
||||||
size: size,
|
size: size,
|
||||||
|
position: position,
|
||||||
) as int;
|
) as int;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1190,7 +1193,8 @@ abstract class _AndroidViewControllerInternals {
|
|||||||
required bool hybrid,
|
required bool hybrid,
|
||||||
bool hybridFallback = false,
|
bool hybridFallback = false,
|
||||||
_CreationParams? creationParams,
|
_CreationParams? creationParams,
|
||||||
Size? size}) {
|
Size? size,
|
||||||
|
Offset? position}) {
|
||||||
final Map<String, dynamic> args = <String, dynamic>{
|
final Map<String, dynamic> args = <String, dynamic>{
|
||||||
'id': viewId,
|
'id': viewId,
|
||||||
'viewType': viewType,
|
'viewType': viewType,
|
||||||
@ -1199,6 +1203,8 @@ abstract class _AndroidViewControllerInternals {
|
|||||||
if (size != null) 'width': size.width,
|
if (size != null) 'width': size.width,
|
||||||
if (size != null) 'height': size.height,
|
if (size != null) 'height': size.height,
|
||||||
if (hybridFallback == true) 'hybridFallback': hybridFallback,
|
if (hybridFallback == true) 'hybridFallback': hybridFallback,
|
||||||
|
if (position != null) 'left': position.dx,
|
||||||
|
if (position != null) 'top': position.dy,
|
||||||
};
|
};
|
||||||
if (creationParams != null) {
|
if (creationParams != null) {
|
||||||
final ByteData paramsByteData = creationParams.codec.encodeMessage(creationParams.data)!;
|
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] is the view's initial size in logical pixel.
|
||||||
/// [size] can be omitted if the concrete implementation doesn't require an initial size
|
/// [size] can be omitted if the concrete implementation doesn't require an initial size
|
||||||
/// to create the platform view.
|
/// 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.
|
/// Disposes the platform view.
|
||||||
///
|
///
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
|
import 'package:flutter/scheduler.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
import 'basic.dart';
|
import 'basic.dart';
|
||||||
@ -879,9 +880,9 @@ class _PlatformViewLinkState extends State<PlatformViewLink> {
|
|||||||
if (!_platformViewCreated) {
|
if (!_platformViewCreated) {
|
||||||
// Depending on the implementation, the first non-empty size can be used
|
// Depending on the implementation, the first non-empty size can be used
|
||||||
// to size the platform view.
|
// to size the platform view.
|
||||||
return _PlatformViewPlaceHolder(onLayout: (Size size) {
|
return _PlatformViewPlaceHolder(onLayout: (Size size, Offset position) {
|
||||||
if (controller.awaitingCreation && !size.isEmpty) {
|
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.
|
/// A callback used to notify the size of the platform view placeholder.
|
||||||
/// This size is the initial size of the platform view.
|
/// 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.
|
/// A [RenderBox] that notifies its size to the owner after a layout.
|
||||||
class _PlatformViewPlaceholderBox extends RenderConstrainedBox {
|
class _PlatformViewPlaceholderBox extends RenderConstrainedBox {
|
||||||
@ -1204,7 +1205,10 @@ class _PlatformViewPlaceholderBox extends RenderConstrainedBox {
|
|||||||
@override
|
@override
|
||||||
void performLayout() {
|
void performLayout() {
|
||||||
super.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;
|
bool _createCalledSuccessfully = false;
|
||||||
|
|
||||||
|
Offset? createPosition;
|
||||||
|
|
||||||
final List<PlatformViewCreatedCallback> _createdCallbacks = <PlatformViewCreatedCallback>[];
|
final List<PlatformViewCreatedCallback> _createdCallbacks = <PlatformViewCreatedCallback>[];
|
||||||
|
|
||||||
/// Events that are dispatched.
|
/// Events that are dispatched.
|
||||||
@ -131,12 +133,13 @@ class FakeAndroidViewController implements AndroidViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> create({Size? size}) async {
|
Future<void> create({Size? size, Offset? position}) async {
|
||||||
assert(!_createCalledSuccessfully);
|
assert(!_createCalledSuccessfully);
|
||||||
if (requiresSize && size != null) {
|
if (requiresSize && size != null) {
|
||||||
assert(!size.isEmpty);
|
assert(!size.isEmpty);
|
||||||
}
|
}
|
||||||
_createCalledSuccessfully = size != null || !requiresSize;
|
_createCalledSuccessfully = size != null && position != null || !requiresSize;
|
||||||
|
createPosition = position;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -214,6 +217,8 @@ class FakeAndroidPlatformViewsController {
|
|||||||
final bool? hybrid = args['hybrid'] as bool?;
|
final bool? hybrid = args['hybrid'] as bool?;
|
||||||
final bool? hybridFallback = args['hybridFallback'] as bool?;
|
final bool? hybridFallback = args['hybridFallback'] as bool?;
|
||||||
final Uint8List? creationParams = args['params'] as Uint8List?;
|
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)) {
|
if (_views.containsKey(id)) {
|
||||||
throw PlatformException(
|
throw PlatformException(
|
||||||
@ -239,6 +244,7 @@ class FakeAndroidPlatformViewsController {
|
|||||||
hybrid: hybrid,
|
hybrid: hybrid,
|
||||||
hybridFallback: hybridFallback,
|
hybridFallback: hybridFallback,
|
||||||
creationParams: creationParams,
|
creationParams: creationParams,
|
||||||
|
position: left != null && top != null ? Offset(left, top) : null,
|
||||||
);
|
);
|
||||||
// Return a hybrid result (null rather than a texture ID) if:
|
// Return a hybrid result (null rather than a texture ID) if:
|
||||||
final bool hybridResult =
|
final bool hybridResult =
|
||||||
@ -538,7 +544,7 @@ class FakeHtmlPlatformViewsController {
|
|||||||
@immutable
|
@immutable
|
||||||
class FakeAndroidPlatformView {
|
class FakeAndroidPlatformView {
|
||||||
const FakeAndroidPlatformView(this.id, this.type, this.size, this.layoutDirection,
|
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 int id;
|
||||||
final String type;
|
final String type;
|
||||||
@ -547,6 +553,7 @@ class FakeAndroidPlatformView {
|
|||||||
final int layoutDirection;
|
final int layoutDirection;
|
||||||
final bool? hybrid;
|
final bool? hybrid;
|
||||||
final bool? hybridFallback;
|
final bool? hybridFallback;
|
||||||
|
final Offset? position;
|
||||||
|
|
||||||
FakeAndroidPlatformView copyWith({Size? size, int? layoutDirection}) => FakeAndroidPlatformView(
|
FakeAndroidPlatformView copyWith({Size? size, int? layoutDirection}) => FakeAndroidPlatformView(
|
||||||
id,
|
id,
|
||||||
@ -556,6 +563,7 @@ class FakeAndroidPlatformView {
|
|||||||
hybrid: hybrid,
|
hybrid: hybrid,
|
||||||
hybridFallback: hybridFallback,
|
hybridFallback: hybridFallback,
|
||||||
creationParams: creationParams,
|
creationParams: creationParams,
|
||||||
|
position: position,
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -570,7 +578,8 @@ class FakeAndroidPlatformView {
|
|||||||
&& other.size == size
|
&& other.size == size
|
||||||
&& other.hybrid == hybrid
|
&& other.hybrid == hybrid
|
||||||
&& other.hybridFallback == hybridFallback
|
&& other.hybridFallback == hybridFallback
|
||||||
&& other.layoutDirection == layoutDirection;
|
&& other.layoutDirection == layoutDirection
|
||||||
|
&& other.position == position;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -582,13 +591,14 @@ class FakeAndroidPlatformView {
|
|||||||
layoutDirection,
|
layoutDirection,
|
||||||
hybrid,
|
hybrid,
|
||||||
hybridFallback,
|
hybridFallback,
|
||||||
|
position,
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'FakeAndroidPlatformView(id: $id, type: $type, size: $size, '
|
return 'FakeAndroidPlatformView(id: $id, type: $type, size: $size, '
|
||||||
'layoutDirection: $layoutDirection, hybrid: $hybrid, '
|
'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(
|
testWidgets(
|
||||||
'PlatformViewLink does not double-call create for Android Hybrid Composition',
|
'PlatformViewLink does not double-call create for Android Hybrid Composition',
|
||||||
(WidgetTester tester) async {
|
(WidgetTester tester) async {
|
||||||
@ -2616,7 +2661,7 @@ void main() {
|
|||||||
controller = FakeAndroidViewController(params.id);
|
controller = FakeAndroidViewController(params.id);
|
||||||
controller.create();
|
controller.create();
|
||||||
// This test should be simulating Hybrid Composition mode, where
|
// This test should be simulating Hybrid Composition mode, where
|
||||||
// `create` takes effect immidately.
|
// `create` takes effect immediately.
|
||||||
expect(controller.awaitingCreation, false);
|
expect(controller.awaitingCreation, false);
|
||||||
return controller;
|
return controller;
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user