Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit 61c30ee

Browse files
author
Emmanuel Garcia
authored
Reland: "Use texture layer when displaying an Android view" (#100934)
1 parent eb8a474 commit 61c30ee

File tree

11 files changed

+298
-234
lines changed

11 files changed

+298
-234
lines changed

dev/integration_tests/hybrid_android_views/android/app/src/main/AndroidManifest.xml

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ found in the LICENSE file. -->
1616
android:launchMode="singleTop"
1717
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density"
1818
android:hardwareAccelerated="true"
19-
android:windowSoftInputMode="adjustResize">
19+
android:windowSoftInputMode="adjustResize"
20+
android:exported="true">
2021
<meta-data
2122
android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
2223
android:value="true" />
@@ -28,9 +29,5 @@ found in the LICENSE file. -->
2829
<meta-data
2930
android:name="flutterEmbedding"
3031
android:value="2" />
31-
<!-- Hybrid composition -->
32-
<meta-data
33-
android:name="io.flutter.embedded_views_preview"
34-
android:value="true" />
3532
</application>
3633
</manifest>

dev/integration_tests/hybrid_android_views/lib/android_platform_view.dart

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class AndroidPlatformView extends StatelessWidget {
1717
const AndroidPlatformView({
1818
Key? key,
1919
this.onPlatformViewCreated,
20+
this.useHybridComposition = false,
2021
required this.viewType,
2122
}) : assert(viewType != null),
2223
super(key: key);
@@ -31,6 +32,9 @@ class AndroidPlatformView extends StatelessWidget {
3132
/// May be null.
3233
final PlatformViewCreatedCallback? onPlatformViewCreated;
3334

35+
// Use hybrid composition.
36+
final bool useHybridComposition;
37+
3438
@override
3539
Widget build(BuildContext context) {
3640
return PlatformViewLink(
@@ -44,17 +48,27 @@ class AndroidPlatformView extends StatelessWidget {
4448
);
4549
},
4650
onCreatePlatformView: (PlatformViewCreationParams params) {
47-
final AndroidViewController controller =
48-
PlatformViewsService.initSurfaceAndroidView(
51+
print('useHybridComposition=$useHybridComposition');
52+
late AndroidViewController controller;
53+
if (useHybridComposition) {
54+
controller = PlatformViewsService.initExpensiveAndroidView(
4955
id: params.id,
5056
viewType: params.viewType,
5157
layoutDirection: TextDirection.ltr,
52-
)
53-
..addOnPlatformViewCreatedListener(params.onPlatformViewCreated);
58+
);
59+
} else {
60+
controller = PlatformViewsService.initSurfaceAndroidView(
61+
id: params.id,
62+
viewType: params.viewType,
63+
layoutDirection: TextDirection.ltr,
64+
);
65+
}
5466
if (onPlatformViewCreated != null) {
5567
controller.addOnPlatformViewCreatedListener(onPlatformViewCreated!);
5668
}
57-
return controller..create();
69+
return controller
70+
..addOnPlatformViewCreatedListener(params.onPlatformViewCreated)
71+
..create();
5872
},
5973
);
6074
}

dev/integration_tests/hybrid_android_views/lib/nested_view_event_page.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class NestedViewEventBodyState extends State<NestedViewEventBody> {
3939
int? id;
4040
int nestedViewClickCount = 0;
4141
bool showPlatformView = true;
42+
bool useHybridComposition = false;
4243

4344
@override
4445
Widget build(BuildContext context) {
@@ -55,6 +56,7 @@ class NestedViewEventBodyState extends State<NestedViewEventBody> {
5556
key: const ValueKey<String>('PlatformView'),
5657
viewType: 'simple_view',
5758
onPlatformViewCreated: onPlatformViewCreated,
59+
useHybridComposition: useHybridComposition,
5860
) : null,
5961
),
6062
if (_lastTestStatus != _LastTestStatus.pending) _statusWidget(),
@@ -69,6 +71,15 @@ class NestedViewEventBodyState extends State<NestedViewEventBody> {
6971
onPressed: onTogglePlatformView,
7072
child: const Text('TOGGLE PLATFORM VIEW'),
7173
),
74+
ElevatedButton(
75+
key: const ValueKey<String>('ToggleHybridComposition'),
76+
child: const Text('TOGGLE HYBRID COMPOSITION'),
77+
onPressed: () {
78+
setState(() {
79+
useHybridComposition = !useHybridComposition;
80+
});
81+
},
82+
),
7283
Row(
7384
children: <Widget>[
7485
ElevatedButton(

dev/integration_tests/hybrid_android_views/test_driver/main_test.dart

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,58 @@ Future<void> main() async {
5959
}, timeout: Timeout.none);
6060
});
6161

62-
group('Flutter surface switch', () {
62+
group('Flutter surface without hybrid composition', () {
6363
setUpAll(() async {
64-
final SerializableFinder wmListTile = find.byValueKey('NestedViewEventTile');
65-
await driver.tap(wmListTile);
64+
await driver.tap(find.byValueKey('NestedViewEventTile'));
65+
});
66+
67+
tearDownAll(() async {
68+
await driver.waitFor(find.pageBack());
69+
await driver.tap(find.pageBack());
70+
});
71+
72+
test('Uses FlutterSurfaceView when Android view is on the screen', () async {
73+
await driver.waitFor(find.byValueKey('PlatformView'));
74+
75+
expect(
76+
await driver.requestData('hierarchy'),
77+
'|-FlutterView\n'
78+
' |-FlutterSurfaceView\n' // Flutter UI
79+
' |-ViewGroup\n' // Platform View
80+
' |-ViewGroup\n'
81+
);
82+
83+
// Hide platform view.
84+
final SerializableFinder togglePlatformView = find.byValueKey('TogglePlatformView');
85+
await driver.tap(togglePlatformView);
86+
await driver.waitForAbsent(find.byValueKey('PlatformView'));
87+
88+
expect(
89+
await driver.requestData('hierarchy'),
90+
'|-FlutterView\n'
91+
' |-FlutterSurfaceView\n' // Just the Flutter UI
92+
);
93+
94+
// Show platform view again.
95+
await driver.tap(togglePlatformView);
96+
await driver.waitFor(find.byValueKey('PlatformView'));
97+
98+
expect(
99+
await driver.requestData('hierarchy'),
100+
'|-FlutterView\n'
101+
' |-FlutterSurfaceView\n' // Flutter UI
102+
' |-ViewGroup\n' // Platform View
103+
' |-ViewGroup\n'
104+
);
105+
}, timeout: Timeout.none);
106+
});
107+
108+
group('Flutter surface with hybrid composition', () {
109+
setUpAll(() async {
110+
await driver.tap(find.byValueKey('NestedViewEventTile'));
111+
await driver.tap(find.byValueKey('ToggleHybridComposition'));
112+
await driver.tap(find.byValueKey('TogglePlatformView'));
113+
await driver.tap(find.byValueKey('TogglePlatformView'));
66114
});
67115

68116
tearDownAll(() async {

packages/flutter/lib/src/rendering/platform_view.dart

Lines changed: 41 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ Set<Type> _factoriesTypeSet<T>(Set<Factory<T>> factories) {
5252

5353
/// A render object for an Android view.
5454
///
55-
/// Requires Android API level 20 or greater.
55+
/// Requires Android API level 23 or greater.
5656
///
5757
/// [RenderAndroidView] is responsible for sizing, displaying and passing touch events to an
5858
/// Android [View](https://developer.android.com/reference/android/view/View).
@@ -74,7 +74,7 @@ Set<Type> _factoriesTypeSet<T>(Set<Factory<T>> factories) {
7474
///
7575
/// * [AndroidView] which is a widget that is used to show an Android view.
7676
/// * [PlatformViewsService] which is a service for controlling platform views.
77-
class RenderAndroidView extends RenderBox with _PlatformViewGestureMixin {
77+
class RenderAndroidView extends PlatformViewRenderBox {
7878
/// Creates a render object for an Android view.
7979
RenderAndroidView({
8080
required AndroidViewController viewController,
@@ -86,7 +86,8 @@ class RenderAndroidView extends RenderBox with _PlatformViewGestureMixin {
8686
assert(gestureRecognizers != null),
8787
assert(clipBehavior != null),
8888
_viewController = viewController,
89-
_clipBehavior = clipBehavior {
89+
_clipBehavior = clipBehavior,
90+
super(controller: viewController, hitTestBehavior: hitTestBehavior, gestureRecognizers: gestureRecognizers) {
9091
_viewController.pointTransformer = (Offset offset) => globalToLocal(offset);
9192
updateGestureRecognizers(gestureRecognizers);
9293
_viewController.addOnPlatformViewCreatedListener(_onPlatformViewCreated);
@@ -101,18 +102,22 @@ class RenderAndroidView extends RenderBox with _PlatformViewGestureMixin {
101102
bool _isDisposed = false;
102103

103104
/// The Android view controller for the Android view associated with this render object.
104-
AndroidViewController get viewController => _viewController;
105+
@override
106+
AndroidViewController get controller => _viewController;
107+
105108
AndroidViewController _viewController;
109+
106110
/// Sets a new Android view controller.
107-
///
108-
/// `viewController` must not be null.
109-
set viewController(AndroidViewController viewController) {
111+
@override
112+
set controller(AndroidViewController controller) {
110113
assert(_viewController != null);
111-
assert(viewController != null);
112-
if (_viewController == viewController)
114+
assert(controller != null);
115+
if (_viewController == controller)
113116
return;
114117
_viewController.removeOnPlatformViewCreatedListener(_onPlatformViewCreated);
115-
_viewController = viewController;
118+
super.controller = controller;
119+
_viewController = controller;
120+
_viewController.pointTransformer = (Offset offset) => globalToLocal(offset);
116121
_sizePlatformView();
117122
if (_viewController.isCreated) {
118123
markNeedsSemanticsUpdate();
@@ -138,26 +143,6 @@ class RenderAndroidView extends RenderBox with _PlatformViewGestureMixin {
138143
markNeedsSemanticsUpdate();
139144
}
140145

141-
/// {@template flutter.rendering.RenderAndroidView.updateGestureRecognizers}
142-
/// Updates which gestures should be forwarded to the platform view.
143-
///
144-
/// Gesture recognizers created by factories in this set participate in the gesture arena for each
145-
/// pointer that was put down on the render box. If any of the recognizers on this list wins the
146-
/// gesture arena, the entire pointer event sequence starting from the pointer down event
147-
/// will be dispatched to the Android view.
148-
///
149-
/// The `gestureRecognizers` property must not contain more than one factory with the same [Factory.type].
150-
///
151-
/// Setting a new set of gesture recognizer factories with the same [Factory.type]s as the current
152-
/// set has no effect, because the factories' constructors would have already been called with the previous set.
153-
/// {@endtemplate}
154-
///
155-
/// Any active gesture arena the Android view participates in is rejected when the
156-
/// set of gesture recognizers is changed.
157-
void updateGestureRecognizers(Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers) {
158-
_updateGestureRecognizersWithCallBack(gestureRecognizers, _viewController.dispatchPointerEvent);
159-
}
160-
161146
@override
162147
bool get sizedByParent => true;
163148

@@ -182,9 +167,8 @@ class RenderAndroidView extends RenderBox with _PlatformViewGestureMixin {
182167
// Android virtual displays cannot have a zero size.
183168
// Trying to size it to 0 crashes the app, which was happening when starting the app
184169
// with a locked screen (see: https://github.com/flutter/flutter/issues/20456).
185-
if (_state == _PlatformViewState.resizing || size.isEmpty) {
170+
if (_state == _PlatformViewState.resizing || size.isEmpty)
186171
return;
187-
}
188172

189173
_state = _PlatformViewState.resizing;
190174
markNeedsPaint();
@@ -212,7 +196,8 @@ class RenderAndroidView extends RenderBox with _PlatformViewGestureMixin {
212196
void _setOffset() {
213197
SchedulerBinding.instance.addPostFrameCallback((_) async {
214198
if (!_isDisposed) {
215-
await _viewController.setOffset(localToGlobal(Offset.zero));
199+
if (attached)
200+
await _viewController.setOffset(localToGlobal(Offset.zero));
216201
// Schedule a new post frame callback.
217202
_setOffset();
218203
}
@@ -221,7 +206,7 @@ class RenderAndroidView extends RenderBox with _PlatformViewGestureMixin {
221206

222207
@override
223208
void paint(PaintingContext context, Offset offset) {
224-
if (_viewController.textureId == null)
209+
if (_viewController.textureId == null || _currentTextureSize == null)
225210
return;
226211

227212
// 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 {
264249

265250
context.addLayer(TextureLayer(
266251
rect: offset & _currentTextureSize!,
267-
textureId: viewController.textureId!,
252+
textureId: _viewController.textureId!,
268253
));
269254
}
270255

271256
@override
272-
void describeSemanticsConfiguration (SemanticsConfiguration config) {
273-
super.describeSemanticsConfiguration(config);
274-
257+
void describeSemanticsConfiguration(SemanticsConfiguration config) {
258+
// Don't call the super implementation since `platformViewId` should
259+
// be set only when the platform view is created, but the concept of
260+
// a "created" platform view belongs to this subclass.
275261
config.isSemanticBoundary = true;
276262

277263
if (_viewController.isCreated) {
@@ -339,7 +325,7 @@ class RenderUiKitView extends RenderBox {
339325
// any newly arriving events there's nothing we need to invalidate.
340326
PlatformViewHitTestBehavior hitTestBehavior;
341327

342-
/// {@macro flutter.rendering.RenderAndroidView.updateGestureRecognizers}
328+
/// {@macro flutter.rendering.PlatformViewRenderBox.updateGestureRecognizers}
343329
void updateGestureRecognizers(Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers) {
344330
assert(gestureRecognizers != null);
345331
assert(
@@ -653,11 +639,11 @@ class PlatformViewRenderBox extends RenderBox with _PlatformViewGestureMixin {
653639
PlatformViewController get controller => _controller;
654640
PlatformViewController _controller;
655641
/// This value must not be null, and setting it to a new value will result in a repaint.
656-
set controller(PlatformViewController controller) {
642+
set controller(covariant PlatformViewController controller) {
657643
assert(controller != null);
658644
assert(controller.viewId != null && controller.viewId > -1);
659645

660-
if ( _controller == controller) {
646+
if (_controller == controller) {
661647
return;
662648
}
663649
final bool needsSemanticsUpdate = _controller.viewId != controller.viewId;
@@ -668,7 +654,19 @@ class PlatformViewRenderBox extends RenderBox with _PlatformViewGestureMixin {
668654
}
669655
}
670656

671-
/// {@macro flutter.rendering.RenderAndroidView.updateGestureRecognizers}
657+
/// {@template flutter.rendering.PlatformViewRenderBox.updateGestureRecognizers}
658+
/// Updates which gestures should be forwarded to the platform view.
659+
///
660+
/// Gesture recognizers created by factories in this set participate in the gesture arena for each
661+
/// pointer that was put down on the render box. If any of the recognizers on this list wins the
662+
/// gesture arena, the entire pointer event sequence starting from the pointer down event
663+
/// will be dispatched to the Android view.
664+
///
665+
/// The `gestureRecognizers` property must not contain more than one factory with the same [Factory.type].
666+
///
667+
/// Setting a new set of gesture recognizer factories with the same [Factory.type]s as the current
668+
/// set has no effect, because the factories' constructors would have already been called with the previous set.
669+
/// {@endtemplate}
672670
///
673671
/// Any active gesture arena the `PlatformView` participates in is rejected when the
674672
/// set of gesture recognizers is changed.
@@ -700,7 +698,7 @@ class PlatformViewRenderBox extends RenderBox with _PlatformViewGestureMixin {
700698
}
701699

702700
@override
703-
void describeSemanticsConfiguration (SemanticsConfiguration config) {
701+
void describeSemanticsConfiguration(SemanticsConfiguration config) {
704702
super.describeSemanticsConfiguration(config);
705703
assert(_controller.viewId != null);
706704
config.isSemanticBoundary = true;

0 commit comments

Comments
 (0)