mirror of
https://github.com/flutter/flutter
synced 2024-10-04 07:19:46 +00:00
Reland: "Use texture layer when displaying an Android view" (#100990)
This commit is contained in:
parent
f07f6fef9a
commit
032205eaca
1
.ci.yaml
1
.ci.yaml
|
@ -2023,6 +2023,7 @@ targets:
|
|||
- name: Linux_android hybrid_android_views_integration_test
|
||||
recipe: devicelab/devicelab_drone
|
||||
presubmit: false
|
||||
bringup: true # https://github.com/flutter/flutter/issues/100991
|
||||
timeout: 60
|
||||
properties:
|
||||
tags: >
|
||||
|
|
|
@ -16,7 +16,8 @@ found in the LICENSE file. -->
|
|||
android:launchMode="singleTop"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density"
|
||||
android:hardwareAccelerated="true"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:exported="true">
|
||||
<meta-data
|
||||
android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
|
||||
android:value="true" />
|
||||
|
@ -28,9 +29,5 @@ found in the LICENSE file. -->
|
|||
<meta-data
|
||||
android:name="flutterEmbedding"
|
||||
android:value="2" />
|
||||
<!-- Hybrid composition -->
|
||||
<meta-data
|
||||
android:name="io.flutter.embedded_views_preview"
|
||||
android:value="true" />
|
||||
</application>
|
||||
</manifest>
|
||||
|
|
|
@ -17,6 +17,7 @@ class AndroidPlatformView extends StatelessWidget {
|
|||
const AndroidPlatformView({
|
||||
Key? key,
|
||||
this.onPlatformViewCreated,
|
||||
this.useHybridComposition = false,
|
||||
required this.viewType,
|
||||
}) : assert(viewType != null),
|
||||
super(key: key);
|
||||
|
@ -31,6 +32,9 @@ class AndroidPlatformView extends StatelessWidget {
|
|||
/// May be null.
|
||||
final PlatformViewCreatedCallback? onPlatformViewCreated;
|
||||
|
||||
// Use hybrid composition.
|
||||
final bool useHybridComposition;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return PlatformViewLink(
|
||||
|
@ -44,17 +48,27 @@ class AndroidPlatformView extends StatelessWidget {
|
|||
);
|
||||
},
|
||||
onCreatePlatformView: (PlatformViewCreationParams params) {
|
||||
final AndroidViewController controller =
|
||||
PlatformViewsService.initSurfaceAndroidView(
|
||||
print('useHybridComposition=$useHybridComposition');
|
||||
late AndroidViewController controller;
|
||||
if (useHybridComposition) {
|
||||
controller = PlatformViewsService.initExpensiveAndroidView(
|
||||
id: params.id,
|
||||
viewType: params.viewType,
|
||||
layoutDirection: TextDirection.ltr,
|
||||
)
|
||||
..addOnPlatformViewCreatedListener(params.onPlatformViewCreated);
|
||||
);
|
||||
} else {
|
||||
controller = PlatformViewsService.initSurfaceAndroidView(
|
||||
id: params.id,
|
||||
viewType: params.viewType,
|
||||
layoutDirection: TextDirection.ltr,
|
||||
);
|
||||
}
|
||||
if (onPlatformViewCreated != null) {
|
||||
controller.addOnPlatformViewCreatedListener(onPlatformViewCreated!);
|
||||
}
|
||||
return controller..create();
|
||||
return controller
|
||||
..addOnPlatformViewCreatedListener(params.onPlatformViewCreated)
|
||||
..create();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ class NestedViewEventBodyState extends State<NestedViewEventBody> {
|
|||
int? id;
|
||||
int nestedViewClickCount = 0;
|
||||
bool showPlatformView = true;
|
||||
bool useHybridComposition = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -55,39 +56,63 @@ class NestedViewEventBodyState extends State<NestedViewEventBody> {
|
|||
key: const ValueKey<String>('PlatformView'),
|
||||
viewType: 'simple_view',
|
||||
onPlatformViewCreated: onPlatformViewCreated,
|
||||
useHybridComposition: useHybridComposition,
|
||||
) : null,
|
||||
),
|
||||
if (_lastTestStatus != _LastTestStatus.pending) _statusWidget(),
|
||||
if (viewChannel != null) ... <Widget>[
|
||||
ElevatedButton(
|
||||
key: const ValueKey<String>('ShowAlertDialog'),
|
||||
onPressed: onShowAlertDialogPressed,
|
||||
child: const Text('SHOW ALERT DIALOG'),
|
||||
),
|
||||
ElevatedButton(
|
||||
key: const ValueKey<String>('TogglePlatformView'),
|
||||
onPressed: onTogglePlatformView,
|
||||
child: const Text('TOGGLE PLATFORM VIEW'),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
key: const ValueKey<String>('ShowAlertDialog'),
|
||||
onPressed: onShowAlertDialogPressed,
|
||||
child: const Text('SHOW ALERT DIALOG'),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
key: const ValueKey<String>('TogglePlatformView'),
|
||||
onPressed: onTogglePlatformView,
|
||||
child: const Text('TOGGLE PLATFORM VIEW'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
ElevatedButton(
|
||||
key: const ValueKey<String>('AddChildView'),
|
||||
onPressed: onChildViewPressed,
|
||||
child: const Text('ADD CHILD VIEW'),
|
||||
),
|
||||
ElevatedButton(
|
||||
key: const ValueKey<String>('TapChildView'),
|
||||
onPressed: onTapChildViewPressed,
|
||||
child: const Text('TAP CHILD VIEW'),
|
||||
),
|
||||
if (nestedViewClickCount > 0)
|
||||
Text(
|
||||
'Click count: $nestedViewClickCount',
|
||||
key: const ValueKey<String>('NestedViewClickCount'),
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
key: const ValueKey<String>('ToggleHybridComposition'),
|
||||
child: const Text('TOGGLE HC'),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
useHybridComposition = !useHybridComposition;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
key: const ValueKey<String>('AddChildView'),
|
||||
onPressed: onChildViewPressed,
|
||||
child: const Text('ADD CHILD VIEW'),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
key: const ValueKey<String>('TapChildView'),
|
||||
onPressed: onTapChildViewPressed,
|
||||
child: const Text('TAP CHILD VIEW'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (nestedViewClickCount > 0)
|
||||
Text(
|
||||
'Click count: $nestedViewClickCount',
|
||||
key: const ValueKey<String>('NestedViewClickCount'),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
|
|
|
@ -59,10 +59,58 @@ Future<void> main() async {
|
|||
}, timeout: Timeout.none);
|
||||
});
|
||||
|
||||
group('Flutter surface switch', () {
|
||||
group('Flutter surface without hybrid composition', () {
|
||||
setUpAll(() async {
|
||||
final SerializableFinder wmListTile = find.byValueKey('NestedViewEventTile');
|
||||
await driver.tap(wmListTile);
|
||||
await driver.tap(find.byValueKey('NestedViewEventTile'));
|
||||
});
|
||||
|
||||
tearDownAll(() async {
|
||||
await driver.waitFor(find.pageBack());
|
||||
await driver.tap(find.pageBack());
|
||||
});
|
||||
|
||||
test('Uses FlutterSurfaceView when Android view is on the screen', () async {
|
||||
await driver.waitFor(find.byValueKey('PlatformView'));
|
||||
|
||||
expect(
|
||||
await driver.requestData('hierarchy'),
|
||||
'|-FlutterView\n'
|
||||
' |-FlutterSurfaceView\n' // Flutter UI
|
||||
' |-ViewGroup\n' // Platform View
|
||||
' |-ViewGroup\n'
|
||||
);
|
||||
|
||||
// Hide platform view.
|
||||
final SerializableFinder togglePlatformView = find.byValueKey('TogglePlatformView');
|
||||
await driver.tap(togglePlatformView);
|
||||
await driver.waitForAbsent(find.byValueKey('PlatformView'));
|
||||
|
||||
expect(
|
||||
await driver.requestData('hierarchy'),
|
||||
'|-FlutterView\n'
|
||||
' |-FlutterSurfaceView\n' // Just the Flutter UI
|
||||
);
|
||||
|
||||
// Show platform view again.
|
||||
await driver.tap(togglePlatformView);
|
||||
await driver.waitFor(find.byValueKey('PlatformView'));
|
||||
|
||||
expect(
|
||||
await driver.requestData('hierarchy'),
|
||||
'|-FlutterView\n'
|
||||
' |-FlutterSurfaceView\n' // Flutter UI
|
||||
' |-ViewGroup\n' // Platform View
|
||||
' |-ViewGroup\n'
|
||||
);
|
||||
}, timeout: Timeout.none);
|
||||
});
|
||||
|
||||
group('Flutter surface with hybrid composition', () {
|
||||
setUpAll(() async {
|
||||
await driver.tap(find.byValueKey('NestedViewEventTile'));
|
||||
await driver.tap(find.byValueKey('ToggleHybridComposition'));
|
||||
await driver.tap(find.byValueKey('TogglePlatformView'));
|
||||
await driver.tap(find.byValueKey('TogglePlatformView'));
|
||||
});
|
||||
|
||||
tearDownAll(() async {
|
||||
|
|
|
@ -52,7 +52,7 @@ Set<Type> _factoriesTypeSet<T>(Set<Factory<T>> factories) {
|
|||
|
||||
/// A render object for an Android view.
|
||||
///
|
||||
/// Requires Android API level 20 or greater.
|
||||
/// Requires Android API level 23 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 RenderBox with _PlatformViewGestureMixin {
|
||||
class RenderAndroidView extends PlatformViewRenderBox {
|
||||
/// Creates a render object for an Android view.
|
||||
RenderAndroidView({
|
||||
required AndroidViewController viewController,
|
||||
|
@ -86,7 +86,8 @@ class RenderAndroidView extends RenderBox with _PlatformViewGestureMixin {
|
|||
assert(gestureRecognizers != null),
|
||||
assert(clipBehavior != null),
|
||||
_viewController = viewController,
|
||||
_clipBehavior = clipBehavior {
|
||||
_clipBehavior = clipBehavior,
|
||||
super(controller: viewController, hitTestBehavior: hitTestBehavior, gestureRecognizers: gestureRecognizers) {
|
||||
_viewController.pointTransformer = (Offset offset) => globalToLocal(offset);
|
||||
updateGestureRecognizers(gestureRecognizers);
|
||||
_viewController.addOnPlatformViewCreatedListener(_onPlatformViewCreated);
|
||||
|
@ -101,18 +102,22 @@ class RenderAndroidView extends RenderBox with _PlatformViewGestureMixin {
|
|||
bool _isDisposed = false;
|
||||
|
||||
/// The Android view controller for the Android view associated with this render object.
|
||||
AndroidViewController get viewController => _viewController;
|
||||
@override
|
||||
AndroidViewController get controller => _viewController;
|
||||
|
||||
AndroidViewController _viewController;
|
||||
|
||||
/// Sets a new Android view controller.
|
||||
///
|
||||
/// `viewController` must not be null.
|
||||
set viewController(AndroidViewController viewController) {
|
||||
@override
|
||||
set controller(AndroidViewController controller) {
|
||||
assert(_viewController != null);
|
||||
assert(viewController != null);
|
||||
if (_viewController == viewController)
|
||||
assert(controller != null);
|
||||
if (_viewController == controller)
|
||||
return;
|
||||
_viewController.removeOnPlatformViewCreatedListener(_onPlatformViewCreated);
|
||||
_viewController = viewController;
|
||||
super.controller = controller;
|
||||
_viewController = controller;
|
||||
_viewController.pointTransformer = (Offset offset) => globalToLocal(offset);
|
||||
_sizePlatformView();
|
||||
if (_viewController.isCreated) {
|
||||
markNeedsSemanticsUpdate();
|
||||
|
@ -138,26 +143,6 @@ class RenderAndroidView extends RenderBox with _PlatformViewGestureMixin {
|
|||
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;
|
||||
|
||||
|
@ -182,9 +167,8 @@ class RenderAndroidView extends RenderBox with _PlatformViewGestureMixin {
|
|||
// 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();
|
||||
|
@ -212,7 +196,8 @@ class RenderAndroidView extends RenderBox with _PlatformViewGestureMixin {
|
|||
void _setOffset() {
|
||||
SchedulerBinding.instance.addPostFrameCallback((_) async {
|
||||
if (!_isDisposed) {
|
||||
await _viewController.setOffset(localToGlobal(Offset.zero));
|
||||
if (attached)
|
||||
await _viewController.setOffset(localToGlobal(Offset.zero));
|
||||
// Schedule a new post frame callback.
|
||||
_setOffset();
|
||||
}
|
||||
|
@ -221,7 +206,7 @@ class RenderAndroidView extends RenderBox with _PlatformViewGestureMixin {
|
|||
|
||||
@override
|
||||
void paint(PaintingContext context, Offset offset) {
|
||||
if (_viewController.textureId == null)
|
||||
if (_viewController.textureId == null || _currentTextureSize == null)
|
||||
return;
|
||||
|
||||
// As resizing the Android view happens asynchronously we don't know exactly when is a
|
||||
|
@ -264,14 +249,15 @@ class RenderAndroidView extends RenderBox with _PlatformViewGestureMixin {
|
|||
|
||||
context.addLayer(TextureLayer(
|
||||
rect: offset & _currentTextureSize!,
|
||||
textureId: viewController.textureId!,
|
||||
textureId: _viewController.textureId!,
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
void describeSemanticsConfiguration (SemanticsConfiguration config) {
|
||||
super.describeSemanticsConfiguration(config);
|
||||
|
||||
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.
|
||||
config.isSemanticBoundary = true;
|
||||
|
||||
if (_viewController.isCreated) {
|
||||
|
@ -339,7 +325,7 @@ class RenderUiKitView extends RenderBox {
|
|||
// any newly arriving events there's nothing we need to invalidate.
|
||||
PlatformViewHitTestBehavior hitTestBehavior;
|
||||
|
||||
/// {@macro flutter.rendering.RenderAndroidView.updateGestureRecognizers}
|
||||
/// {@macro flutter.rendering.PlatformViewRenderBox.updateGestureRecognizers}
|
||||
void updateGestureRecognizers(Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers) {
|
||||
assert(gestureRecognizers != null);
|
||||
assert(
|
||||
|
@ -653,11 +639,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(PlatformViewController controller) {
|
||||
set controller(covariant 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;
|
||||
|
@ -668,7 +654,19 @@ class PlatformViewRenderBox extends RenderBox with _PlatformViewGestureMixin {
|
|||
}
|
||||
}
|
||||
|
||||
/// {@macro flutter.rendering.RenderAndroidView.updateGestureRecognizers}
|
||||
/// {@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}
|
||||
///
|
||||
/// Any active gesture arena the `PlatformView` participates in is rejected when the
|
||||
/// set of gesture recognizers is changed.
|
||||
|
@ -700,7 +698,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;
|
||||
|
|
|
@ -76,10 +76,8 @@ class PlatformViewsService {
|
|||
/// The callbacks are invoked when the platform view asks to be focused.
|
||||
final Map<int, VoidCallback> _focusCallbacks = <int, VoidCallback>{};
|
||||
|
||||
|
||||
/// Creates a [TextureAndroidViewController] for a new Android view.
|
||||
///
|
||||
/// The view is created after calling [TextureAndroidViewController.setSize].
|
||||
/// {@template flutter.services.PlatformViewsService.initAndroidView}
|
||||
/// Creates a controller for a new Android view.
|
||||
///
|
||||
/// `id` is an unused unique identifier generated with [platformViewsRegistry].
|
||||
///
|
||||
|
@ -103,7 +101,8 @@ class PlatformViewsService {
|
|||
///
|
||||
/// The `id, `viewType, and `layoutDirection` parameters must not be null.
|
||||
/// If `creationParams` is non null then `creationParamsCodec` must not be null.
|
||||
static TextureAndroidViewController initAndroidView({
|
||||
/// {@endtemplate}
|
||||
static AndroidViewController initAndroidView({
|
||||
required int id,
|
||||
required String viewType,
|
||||
required TextDirection layoutDirection,
|
||||
|
@ -128,32 +127,11 @@ class PlatformViewsService {
|
|||
return controller;
|
||||
}
|
||||
|
||||
/// Creates a [SurfaceAndroidViewController] for a new Android view.
|
||||
/// {@macro flutter.services.PlatformViewsService.initAndroidView}
|
||||
///
|
||||
/// The view is created after calling [AndroidViewController.create].
|
||||
///
|
||||
/// `id` is an unused unique identifier generated with [platformViewsRegistry].
|
||||
///
|
||||
/// `viewType` is the identifier of the Android 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.
|
||||
/// Plugins can register a platform view factory with
|
||||
/// [PlatformViewRegistry#registerViewFactory](/javadoc/io/flutter/plugin/platform/PlatformViewRegistry.html#registerViewFactory-java.lang.String-io.flutter.plugin.platform.PlatformViewFactory-).
|
||||
///
|
||||
/// `creationParams` will be passed as the args argument of [PlatformViewFactory#create](/javadoc/io/flutter/plugin/platform/PlatformViewFactory.html#create-android.content.Context-int-java.lang.Object-)
|
||||
///
|
||||
/// `creationParamsCodec` is the codec used to encode `creationParams` before sending it to the
|
||||
/// platform side. It should match the codec passed to the constructor of [PlatformViewFactory](/javadoc/io/flutter/plugin/platform/PlatformViewFactory.html#PlatformViewFactory-io.flutter.plugin.common.MessageCodec-).
|
||||
/// This is typically one of: [StandardMessageCodec], [JSONMessageCodec], [StringCodec], or [BinaryCodec].
|
||||
///
|
||||
/// `onFocus` is a callback that will be invoked when the Android View asks to get the
|
||||
/// input focus.
|
||||
///
|
||||
/// The Android view will only be created after [AndroidViewController.setSize] is called for the
|
||||
/// first time.
|
||||
///
|
||||
/// The `id, `viewType, and `layoutDirection` parameters must not be null.
|
||||
/// If `creationParams` is non null then `creationParamsCodec` must not be null.
|
||||
/// Alias for [initAndroidView].
|
||||
/// This factory is provided for backward compatibility purposes.
|
||||
/// In the future, this method will be deprecated.
|
||||
static SurfaceAndroidViewController initSurfaceAndroidView({
|
||||
required int id,
|
||||
required String viewType,
|
||||
|
@ -174,28 +152,43 @@ class PlatformViewsService {
|
|||
creationParams: creationParams,
|
||||
creationParamsCodec: creationParamsCodec,
|
||||
);
|
||||
_instance._focusCallbacks[id] = onFocus ?? () {};
|
||||
return controller;
|
||||
}
|
||||
|
||||
/// {@macro flutter.services.PlatformViewsService.initAndroidView}
|
||||
///
|
||||
/// When this factory is used, the Android view and Flutter widgets are composed at the
|
||||
/// Android view hierarchy level.
|
||||
/// This is only useful if the view is a Android SurfaceView. However, using this method
|
||||
/// has a performance cost on devices that run below 10, or underpowered devices.
|
||||
/// In most situations, you should use [initAndroidView].
|
||||
static ExpensiveAndroidViewController initExpensiveAndroidView({
|
||||
required int id,
|
||||
required String viewType,
|
||||
required TextDirection layoutDirection,
|
||||
dynamic creationParams,
|
||||
MessageCodec<dynamic>? creationParamsCodec,
|
||||
VoidCallback? onFocus,
|
||||
}) {
|
||||
final ExpensiveAndroidViewController controller = ExpensiveAndroidViewController._(
|
||||
viewId: id,
|
||||
viewType: viewType,
|
||||
layoutDirection: layoutDirection,
|
||||
creationParams: creationParams,
|
||||
creationParamsCodec: creationParamsCodec,
|
||||
);
|
||||
|
||||
_instance._focusCallbacks[id] = onFocus ?? () {};
|
||||
return controller;
|
||||
}
|
||||
|
||||
/// Whether the render surface of the Android `FlutterView` should be converted to a `FlutterImageView`.
|
||||
///
|
||||
/// When adding platform views using
|
||||
/// [Hybrid Composition](https://flutter.dev/docs/development/platform-integration/platform-views),
|
||||
/// the engine converts the render surface to a `FlutterImageView` to improve
|
||||
/// animation synchronization between Flutter widgets and the Android platform
|
||||
/// views. On Android versions < 10, this can have some performance issues.
|
||||
/// This flag allows disabling this conversion.
|
||||
///
|
||||
/// Defaults to true.
|
||||
static Future<void> synchronizeToNativeViewHierarchy(bool yes) {
|
||||
assert(defaultTargetPlatform == TargetPlatform.android);
|
||||
return SystemChannels.platform_views.invokeMethod<void>(
|
||||
'synchronizeToNativeViewHierarchy',
|
||||
yes,
|
||||
);
|
||||
}
|
||||
@Deprecated(
|
||||
'No longer necessary to improve performance. '
|
||||
'This feature was deprecated after v2.11.0-0.1.pre.',
|
||||
)
|
||||
static Future<void> synchronizeToNativeViewHierarchy(bool yes) async {}
|
||||
|
||||
// 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.
|
||||
|
@ -665,7 +658,7 @@ class _AndroidMotionEventConverter {
|
|||
event is! PointerDownEvent && event is! PointerUpEvent;
|
||||
}
|
||||
|
||||
/// Controls an Android view.
|
||||
/// Controls an Android view that is composed using a GL texture.
|
||||
///
|
||||
/// Typically created with [PlatformViewsService.initAndroidView].
|
||||
// TODO(bparrishMines): Remove abstract methods that are not required by all subclasses.
|
||||
|
@ -676,7 +669,6 @@ abstract class AndroidViewController extends PlatformViewController {
|
|||
required TextDirection layoutDirection,
|
||||
dynamic creationParams,
|
||||
MessageCodec<dynamic>? creationParamsCodec,
|
||||
bool waitingForSize = false,
|
||||
}) : assert(viewId != null),
|
||||
assert(viewType != null),
|
||||
assert(layoutDirection != null),
|
||||
|
@ -684,10 +676,7 @@ abstract class AndroidViewController extends PlatformViewController {
|
|||
_viewType = viewType,
|
||||
_layoutDirection = layoutDirection,
|
||||
_creationParams = creationParams,
|
||||
_creationParamsCodec = creationParamsCodec,
|
||||
_state = waitingForSize
|
||||
? _AndroidViewState.waitingForSize
|
||||
: _AndroidViewState.creating;
|
||||
_creationParamsCodec = creationParamsCodec;
|
||||
|
||||
/// Action code for when a primary pointer touched the screen.
|
||||
///
|
||||
|
@ -737,7 +726,7 @@ abstract class AndroidViewController extends PlatformViewController {
|
|||
|
||||
TextDirection _layoutDirection;
|
||||
|
||||
_AndroidViewState _state;
|
||||
_AndroidViewState _state = _AndroidViewState.waitingForSize;
|
||||
|
||||
final dynamic _creationParams;
|
||||
|
||||
|
@ -848,10 +837,16 @@ abstract class AndroidViewController extends PlatformViewController {
|
|||
|
||||
/// Removes a callback added with [addOnPlatformViewCreatedListener].
|
||||
void removeOnPlatformViewCreatedListener(PlatformViewCreatedCallback listener) {
|
||||
assert(listener != null);
|
||||
assert(_state != _AndroidViewState.disposed);
|
||||
_platformViewCreatedCallbacks.remove(listener);
|
||||
}
|
||||
|
||||
/// The created callbacks that are invoked after the platform view has been
|
||||
/// created.
|
||||
@visibleForTesting
|
||||
List<PlatformViewCreatedCallback> get createdCallbacks => _platformViewCreatedCallbacks;
|
||||
|
||||
/// Sets the layout direction for the Android view.
|
||||
Future<void> setLayoutDirection(TextDirection layoutDirection) async {
|
||||
assert(
|
||||
|
@ -938,11 +933,29 @@ abstract class AndroidViewController extends PlatformViewController {
|
|||
}
|
||||
}
|
||||
|
||||
/// Controls an Android view by rendering to an [AndroidViewSurface].
|
||||
///
|
||||
/// Typically created with [PlatformViewsService.initAndroidView].
|
||||
class SurfaceAndroidViewController extends AndroidViewController {
|
||||
SurfaceAndroidViewController._({
|
||||
/// Controls an Android view that is composed using a GL texture.
|
||||
/// This controller is created from the [PlatformViewsService.initSurfaceAndroidView] factory,
|
||||
/// and is defined for backward compatibility.
|
||||
class SurfaceAndroidViewController extends TextureAndroidViewController{
|
||||
SurfaceAndroidViewController._({
|
||||
required int viewId,
|
||||
required String viewType,
|
||||
required TextDirection layoutDirection,
|
||||
dynamic creationParams,
|
||||
MessageCodec<dynamic>? creationParamsCodec,
|
||||
}) : super._(
|
||||
viewId: viewId,
|
||||
viewType: viewType,
|
||||
layoutDirection: layoutDirection,
|
||||
creationParams: creationParams,
|
||||
creationParamsCodec: creationParamsCodec,
|
||||
);
|
||||
}
|
||||
|
||||
/// Controls an Android view that is composed using the Android view hierarchy.
|
||||
/// This controller is created from the [PlatformViewsService.initExpensiveAndroidView] factory.
|
||||
class ExpensiveAndroidViewController extends AndroidViewController {
|
||||
ExpensiveAndroidViewController._({
|
||||
required int viewId,
|
||||
required String viewType,
|
||||
required TextDirection layoutDirection,
|
||||
|
@ -1019,7 +1032,6 @@ class TextureAndroidViewController extends AndroidViewController {
|
|||
layoutDirection: layoutDirection,
|
||||
creationParams: creationParams,
|
||||
creationParamsCodec: creationParamsCodec,
|
||||
waitingForSize: true,
|
||||
);
|
||||
|
||||
/// The texture entry id into which the Android view is rendered.
|
||||
|
@ -1032,7 +1044,8 @@ class TextureAndroidViewController extends AndroidViewController {
|
|||
@override
|
||||
int? get textureId => _textureId;
|
||||
|
||||
late Size _initialSize;
|
||||
/// The size used to create the platform view.
|
||||
Size? _initialSize;
|
||||
|
||||
/// The current offset of the platform view.
|
||||
Offset _off = Offset.zero;
|
||||
|
@ -1047,7 +1060,7 @@ class TextureAndroidViewController extends AndroidViewController {
|
|||
if (_state == _AndroidViewState.waitingForSize) {
|
||||
_initialSize = size;
|
||||
await create();
|
||||
return _initialSize;
|
||||
return size;
|
||||
}
|
||||
|
||||
final Map<Object?, Object?>? meta = await SystemChannels.platform_views.invokeMapMethod<Object?, Object?>(
|
||||
|
@ -1093,17 +1106,21 @@ class TextureAndroidViewController extends AndroidViewController {
|
|||
///
|
||||
/// Throws an [AssertionError] if view was already disposed.
|
||||
@override
|
||||
Future<void> create() => super.create();
|
||||
Future<void> create() async {
|
||||
if (_initialSize != null)
|
||||
return super.create();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> _sendCreateMessage() async {
|
||||
assert(!_initialSize.isEmpty, 'trying to create $TextureAndroidViewController without setting a valid size.');
|
||||
assert(_initialSize != null, 'trying to create $TextureAndroidViewController without setting an initial size.');
|
||||
assert(!_initialSize!.isEmpty, 'trying to create $TextureAndroidViewController without setting a valid size.');
|
||||
|
||||
final Map<String, dynamic> args = <String, dynamic>{
|
||||
'id': viewId,
|
||||
'viewType': _viewType,
|
||||
'width': _initialSize.width,
|
||||
'height': _initialSize.height,
|
||||
'width': _initialSize!.width,
|
||||
'height': _initialSize!.height,
|
||||
'direction': AndroidViewController._getAndroidDirection(_layoutDirection),
|
||||
};
|
||||
if (_creationParams != null) {
|
||||
|
|
|
@ -15,7 +15,7 @@ import 'framework.dart';
|
|||
|
||||
/// Embeds an Android view in the Widget hierarchy.
|
||||
///
|
||||
/// Requires Android API level 20 or greater.
|
||||
/// Requires Android API level 23 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.viewController = controller;
|
||||
renderObject.controller = controller;
|
||||
renderObject.hitTestBehavior = hitTestBehavior;
|
||||
renderObject.updateGestureRecognizers(gestureRecognizers);
|
||||
renderObject.clipBehavior = clipBehavior;
|
||||
|
@ -842,15 +842,11 @@ 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,
|
||||
|
@ -875,9 +871,6 @@ 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();
|
||||
}
|
||||
}
|
||||
|
@ -888,16 +881,12 @@ 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();
|
||||
|
@ -1020,18 +1009,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 [PlatformViewLayer] to the layer tree. [PlatformViewLayer]
|
||||
/// isn't supported on all platforms. Custom Flutter embedders can support
|
||||
/// [PlatformViewLayer]s by implementing a SystemCompositor.
|
||||
/// The compositor integration is done by adding a [TextureLayer] to the layer tree.
|
||||
///
|
||||
/// The widget fills all available space, the parent of this object must provide bounded layout
|
||||
/// constraints.
|
||||
/// 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 using a [TextureLayer].
|
||||
/// * [AndroidView] which embeds an Android platform view in the widget hierarchy.
|
||||
/// * [UiKitView] which embeds an iOS platform view in the widget hierarchy.
|
||||
class AndroidViewSurface extends PlatformViewSurface {
|
||||
/// Construct an `AndroidPlatformViewSurface`.
|
||||
|
@ -1052,12 +1041,26 @@ class AndroidViewSurface extends PlatformViewSurface {
|
|||
|
||||
@override
|
||||
RenderObject createRenderObject(BuildContext context) {
|
||||
final PlatformViewRenderBox renderBox =
|
||||
super.createRenderObject(context) as PlatformViewRenderBox;
|
||||
|
||||
(controller as AndroidViewController).pointTransformer =
|
||||
final AndroidViewController viewController = controller as AndroidViewController;
|
||||
// Compose using the Android view hierarchy.
|
||||
// This is useful when embedding a SurfaceView into a Flutter app.
|
||||
// SurfaceViews cannot be composed using GL textures.
|
||||
if (viewController is ExpensiveAndroidViewController) {
|
||||
final PlatformViewRenderBox renderBox =
|
||||
super.createRenderObject(context) as PlatformViewRenderBox;
|
||||
viewController.pointTransformer =
|
||||
(Offset position) => renderBox.globalToLocal(position);
|
||||
return renderBox;
|
||||
}
|
||||
// Use GL texture based composition.
|
||||
// App should use GL texture unless they require to embed a SurfaceView.
|
||||
final RenderAndroidView renderBox = RenderAndroidView(
|
||||
viewController: viewController,
|
||||
gestureRecognizers: gestureRecognizers,
|
||||
hitTestBehavior: hitTestBehavior,
|
||||
);
|
||||
viewController.pointTransformer =
|
||||
(Offset position) => renderBox.globalToLocal(position);
|
||||
|
||||
return renderBox;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -151,6 +151,38 @@ 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,24 +84,23 @@ class FakeAndroidViewController implements AndroidViewController {
|
|||
|
||||
@override
|
||||
Future<Size> setSize(Size size) {
|
||||
throw UnimplementedError();
|
||||
return Future<Size>.value(size);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setOffset(Offset off) {
|
||||
throw UnimplementedError();
|
||||
Future<void> setOffset(Offset off) async {}
|
||||
|
||||
@override
|
||||
int get textureId => 0;
|
||||
|
||||
@override
|
||||
bool get isCreated => created;
|
||||
|
||||
@override
|
||||
void addOnPlatformViewCreatedListener(PlatformViewCreatedCallback listener) {
|
||||
created = true;
|
||||
}
|
||||
|
||||
@override
|
||||
int get textureId => throw UnimplementedError();
|
||||
|
||||
@override
|
||||
bool get isCreated => throw UnimplementedError();
|
||||
|
||||
@override
|
||||
void addOnPlatformViewCreatedListener(PlatformViewCreatedCallback listener) =>
|
||||
throw UnimplementedError();
|
||||
|
||||
@override
|
||||
void removeOnPlatformViewCreatedListener(PlatformViewCreatedCallback listener) {
|
||||
throw UnimplementedError();
|
||||
|
@ -118,9 +117,10 @@ class FakeAndroidViewController implements AndroidViewController {
|
|||
}
|
||||
|
||||
@override
|
||||
Future<void> create() async {
|
||||
created = true;
|
||||
}
|
||||
Future<void> create() async {}
|
||||
|
||||
@override
|
||||
List<PlatformViewCreatedCallback> get createdCallbacks => <PlatformViewCreatedCallback>[];
|
||||
}
|
||||
|
||||
class FakeAndroidPlatformViewsController {
|
||||
|
@ -143,8 +143,6 @@ class FakeAndroidPlatformViewsController {
|
|||
|
||||
int? lastClearedFocusViewId;
|
||||
|
||||
bool synchronizeToNativeViewHierarchy = true;
|
||||
|
||||
Map<int, Offset> offsets = <int, Offset>{};
|
||||
|
||||
void registerViewType(String viewType) {
|
||||
|
@ -174,8 +172,6 @@ class FakeAndroidPlatformViewsController {
|
|||
return _clearFocus(call);
|
||||
case 'offset':
|
||||
return _offset(call);
|
||||
case 'synchronizeToNativeViewHierarchy':
|
||||
return _synchronizeToNativeViewHierarchy(call);
|
||||
}
|
||||
return Future<dynamic>.sync(() => null);
|
||||
}
|
||||
|
@ -318,11 +314,6 @@ 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 {
|
||||
expect(
|
||||
expectLater(
|
||||
() {
|
||||
return PlatformViewsService.initAndroidView(
|
||||
id: 0,
|
||||
|
@ -29,16 +29,25 @@ void main() {
|
|||
throwsA(isA<PlatformException>()),
|
||||
);
|
||||
|
||||
expect(
|
||||
() {
|
||||
return PlatformViewsService.initSurfaceAndroidView(
|
||||
id: 0,
|
||||
viewType: 'web',
|
||||
layoutDirection: TextDirection.ltr,
|
||||
).create();
|
||||
},
|
||||
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`');
|
||||
}
|
||||
});
|
||||
|
||||
test('create Android views', () async {
|
||||
|
@ -47,13 +56,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),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
@ -65,7 +74,7 @@ void main() {
|
|||
viewType: 'webview',
|
||||
layoutDirection: TextDirection.ltr,
|
||||
).setSize(const Size(100.0, 100.0));
|
||||
expect(
|
||||
expectLater(
|
||||
() => PlatformViewsService.initAndroidView(
|
||||
id: 0,
|
||||
viewType: 'web',
|
||||
|
@ -73,20 +82,6 @@ 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 {
|
||||
|
@ -240,11 +235,6 @@ 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,43 +2312,6 @@ 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(
|
||||
|
|
Loading…
Reference in a new issue