mirror of
https://github.com/flutter/flutter.git
synced 2026-01-09 07:51:35 +08:00
Replace Hybrid Composition wiki page with dev-facing content (#180642)
The wiki is supposed to be for team-focused documentation, but this page was client-focused. That was resolved at some point via the creation of https://docs.flutter.dev/platform-integration/android/platform-views, https://docs.flutter.dev/platform-integration/ios/platform-views, and https://docs.flutter.dev/platform-integration/macos/platform-views, and this page is now a somewhat outdated (e.g., in doesn't use code excerpting to ensure correctness like the docs page does), less client-friendly version of those pages. This removes essentially all of the duplicated content, and makes it a much smaller, team-focused page, following the format of the corresponding TLHC page in the wiki. Since there are likely external pages that linked here for the old content, this leaves a breadcrumb to the client-facing docs at the top. Fixes https://github.com/flutter/flutter/issues/124801 --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
This commit is contained in:
parent
3a907d98fb
commit
01d37bc80f
@ -1,394 +1,44 @@
|
||||
Hybrid composition refers to the ability of composing native views alongside Flutter widgets. For example, displaying the native Webview inside a Flutter app.
|
||||
|
||||
## Android
|
||||
*Requires API level 19*
|
||||
|
||||
_See also: [Texture Layer Hybrid Composition](./android/Texture-Layer-Hybrid-Composition.md)_
|
||||
|
||||
Starting from Flutter 1.20.0, hybrid composition can be used on Android. This new feature fixes most of the [issues with the preview platform view approach](./android/Virtual-Display.md#associated-problems-and-workarounds) (Virtual Display); in particular, accessibility and keyboard related issues. See also [Android Platform Views](./android/Android-Platform-Views.md) for an overview of modes.
|
||||
|
||||
To see all known issues specific to this mode, search for the [`hc-only` label](https://github.com/flutter/flutter/labels/hc-only).
|
||||
|
||||
### Dart side
|
||||
|
||||
To start using this feature, you would need to create a `Widget`, and add the following `build` implementation:
|
||||
|
||||
`native_view_example.dart`
|
||||
|
||||
1. Add imports:
|
||||
```dart
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
```
|
||||
|
||||
2. Implement `build` method:
|
||||
```dart
|
||||
Widget build(BuildContext context) {
|
||||
// This is used in the platform side to register the view.
|
||||
final String viewType = 'hybrid-view-type';
|
||||
// Pass parameters to the platform side.
|
||||
final Map<String, dynamic> creationParams = <String, dynamic>{};
|
||||
|
||||
return PlatformViewLink(
|
||||
viewType: viewType,
|
||||
surfaceFactory:
|
||||
(BuildContext context, PlatformViewController controller) {
|
||||
return AndroidViewSurface(
|
||||
controller: controller,
|
||||
gestureRecognizers: const <Factory<OneSequenceGestureRecognizer>>{},
|
||||
hitTestBehavior: PlatformViewHitTestBehavior.opaque,
|
||||
);
|
||||
},
|
||||
onCreatePlatformView: (PlatformViewCreationParams params) {
|
||||
return PlatformViewsService.initSurfaceAndroidView(
|
||||
id: params.id,
|
||||
viewType: viewType,
|
||||
layoutDirection: TextDirection.ltr,
|
||||
creationParams: creationParams,
|
||||
creationParamsCodec: StandardMessageCodec(),
|
||||
)
|
||||
..addOnPlatformViewCreatedListener(params.onPlatformViewCreated)
|
||||
..create();
|
||||
},
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
For more documentation see: [PlatformViewLink](https://api.flutter.dev/flutter/widgets/PlatformViewLink-class.html), [AndroidViewSurface](https://api.flutter.dev/flutter/widgets/AndroidViewSurface-class.html), [PlatformViewsService](https://api.flutter.dev/flutter/services/PlatformViewsService-class.html).
|
||||
|
||||
### Platform side
|
||||
|
||||
Finally, on the platform side, you use the standard `io.flutter.plugin.platform` package in Java or Kotlin:
|
||||
|
||||
`NativeView.java`
|
||||
|
||||
```java
|
||||
package dev.flutter.example;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import io.flutter.plugin.platform.PlatformView;
|
||||
|
||||
class NativeView implements PlatformView {
|
||||
@NonNull private final TextView textView;
|
||||
|
||||
NativeView(@NonNull Context context, int id, @Nullable Map<String, Object> creationParams) {
|
||||
textView = new TextView(context);
|
||||
textView.setTextSize(72);
|
||||
textView.setBackgroundColor(Color.rgb(255, 255, 255));
|
||||
textView.setText("Rendered on a native Android view (id: " + id + ")");
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public View getView() {
|
||||
return textView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {}
|
||||
}
|
||||
```
|
||||
|
||||
`NativeViewFactory.java`
|
||||
|
||||
```java
|
||||
package dev.flutter.example;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.NonNull;
|
||||
import io.flutter.plugin.common.BinaryMessenger;
|
||||
import io.flutter.plugin.common.StandardMessageCodec;
|
||||
import io.flutter.plugin.platform.PlatformView;
|
||||
import io.flutter.plugin.platform.PlatformViewFactory;
|
||||
import java.util.Map;
|
||||
|
||||
class NativeViewFactory extends PlatformViewFactory {
|
||||
@NonNull private final BinaryMessenger messenger;
|
||||
@NonNull private final View containerView;
|
||||
|
||||
NativeViewFactory(@NonNull BinaryMessenger messenger, @NonNull View containerView) {
|
||||
super(StandardMessageCodec.INSTANCE);
|
||||
this.messenger = messenger;
|
||||
this.containerView = containerView;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public PlatformView create(@NonNull Context context, int id, @Nullable Object args) {
|
||||
final Map<String, Object> creationParams = (Map<String, Object>) args;
|
||||
return new NativeView(context, id, creationParams);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Register the platform view. This can be done in an app or a plugin.
|
||||
|
||||
For app registration, modify the main activity (e.g. `MainActivity.java`):
|
||||
|
||||
```java
|
||||
package dev.flutter.example;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import io.flutter.embedding.android.FlutterActivity;
|
||||
import io.flutter.embedding.engine.FlutterEngine;
|
||||
|
||||
public class MainActivity extends FlutterActivity {
|
||||
@Override
|
||||
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
|
||||
flutterEngine
|
||||
.getPlatformViewsController()
|
||||
.getRegistry()
|
||||
.registerViewFactory("<platform-view-type>", new NativeViewFactory());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For plugin registration, modify the main plugin file (e.g. `PlatformViewPlugin.java`):
|
||||
|
||||
```java
|
||||
package dev.flutter.plugin.example;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import io.flutter.embedding.engine.plugins.FlutterPlugin;
|
||||
import io.flutter.plugin.common.BinaryMessenger;
|
||||
|
||||
public class PlatformViewPlugin implements FlutterPlugin {
|
||||
@Override
|
||||
public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
|
||||
binding
|
||||
.getFlutterEngine()
|
||||
.getPlatformViewsController()
|
||||
.getRegistry()
|
||||
.registerViewFactory("<platform-view-type>", new NativeViewFactory());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For more documentation, see [PlatformViewRegistry](https://api.flutter.dev/javadoc/io/flutter/plugin/platform/PlatformViewRegistry.html), [PlatformViewFactory](https://api.flutter.dev/javadoc/io/flutter/plugin/platform/PlatformViewFactory.html), and [PlatformView](https://api.flutter.dev/javadoc/io/flutter/plugin/platform/PlatformView.html).
|
||||
|
||||
Finally, indicate the minimum API Level required for the application to run in `build.gradle`.
|
||||
|
||||
```groovy
|
||||
android {
|
||||
defaultConfig {
|
||||
minSdkVersion 19
|
||||
}
|
||||
}
|
||||
```
|
||||
## iOS
|
||||
|
||||
In Flutter 1.22, platform views are enabled by default. This means
|
||||
that it's no longer required to add the
|
||||
`io.flutter.embedded_views_preview` flag to `Info.plist`.
|
||||
|
||||
To create a platform view on iOS, follow these steps:
|
||||
|
||||
### On the Dart side
|
||||
|
||||
On the Dart side, create a `Widget`
|
||||
and add the following build implementation,
|
||||
as shown in the following steps.
|
||||
|
||||
In your Dart file, for example `native_view_example.dart`,
|
||||
do the following:
|
||||
|
||||
1. Add the following imports:
|
||||
|
||||
<!-- skip -->
|
||||
```dart
|
||||
import 'package:flutter/widget.dart';
|
||||
```
|
||||
|
||||
|
||||
2. Implement a `build()` method:
|
||||
|
||||
<!-- skip -->
|
||||
```dart
|
||||
Widget build(BuildContext context) {
|
||||
// This is used in the platform side to register the view.
|
||||
final String viewType = '<platform-view-type>';
|
||||
// Pass parameters to the platform side.
|
||||
final Map<String, dynamic> creationParams = <String, dynamic>{};
|
||||
|
||||
return UiKitView(
|
||||
viewType: viewType,
|
||||
layoutDirection: TextDirection.ltr,
|
||||
creationParams: creationParams,
|
||||
creationParamsCodec: const StandardMessageCodec(),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
For more information, see the API docs for:
|
||||
[`UIKitView`](https://api.flutter.dev/flutter/widgets/UiKitView-class.html).
|
||||
|
||||
### On the platform side
|
||||
|
||||
In your native code, implement the following:
|
||||
|
||||
`FLNativeView.h`
|
||||
|
||||
```objc
|
||||
#import <Flutter/Flutter.h>
|
||||
|
||||
@interface FLNativeViewFactory : NSObject <FlutterPlatformViewFactory>
|
||||
- (instancetype)initWithMessenger:(NSObject<FlutterBinaryMessenger>*)messenger;
|
||||
@end
|
||||
|
||||
@interface FLNativeView : NSObject <FlutterPlatformView>
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
viewIdentifier:(int64_t)viewId
|
||||
arguments:(id _Nullable)args
|
||||
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger;
|
||||
|
||||
- (UIView*)view;
|
||||
@end
|
||||
```
|
||||
|
||||
Implement the factory and the platform view in `FLNativeView.m`
|
||||
|
||||
```objc
|
||||
#import "FLNativeView.h"
|
||||
|
||||
@implementation FLNativeViewFactory {
|
||||
NSObject<FlutterBinaryMessenger>* _messenger;
|
||||
}
|
||||
|
||||
- (instancetype)initWithMessenger:(NSObject<FlutterBinaryMessenger>*)messenger {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_messenger = messenger;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSObject<FlutterPlatformView>*)createWithFrame:(CGRect)frame
|
||||
viewIdentifier:(int64_t)viewId
|
||||
arguments:(id _Nullable)args {
|
||||
return [[FLNativeView alloc] initWithFrame:frame
|
||||
viewIdentifier:viewId
|
||||
arguments:args
|
||||
binaryMessenger:_messenger];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation FLNativeView {
|
||||
UIView *_view;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
viewIdentifier:(int64_t)viewId
|
||||
arguments:(id _Nullable)args
|
||||
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger {
|
||||
if (self = [super init]) {
|
||||
_view = [[UIView alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (UIView*)view {
|
||||
return _view;
|
||||
}
|
||||
|
||||
@end
|
||||
```
|
||||
|
||||
Finally, register the platform view. This can be done in an app or a plugin.
|
||||
|
||||
|
||||
For app registration, modify the App's `AppDelegate.m`:
|
||||
|
||||
```objc
|
||||
#import "AppDelegate.h"
|
||||
#import "FLNativeView.h"
|
||||
#import "GeneratedPluginRegistrant.h"
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
- (BOOL)application:(UIApplication *)application
|
||||
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
||||
[GeneratedPluginRegistrant registerWithRegistry:self];
|
||||
|
||||
NSObject<FlutterPluginRegistrar>* registrar =
|
||||
[self registrarForPlugin:@"plugin-name"];
|
||||
|
||||
FLNativeViewFactory* factory =
|
||||
[[FLNativeViewFactory alloc] initWithMessenger:registrar.messenger];
|
||||
[[self registrarForPlugin:@"<plugin-name>"] registerViewFactory:factory
|
||||
withId:@"<platform-view-type>"];
|
||||
return [super application:application didFinishLaunchingWithOptions:launchOptions];
|
||||
}
|
||||
|
||||
@end
|
||||
```
|
||||
|
||||
For plugin registration, modify the main plugin file (e.g. `FLPlugin.m`):
|
||||
|
||||
```objc
|
||||
#import "FLPlugin.h"
|
||||
#import "FLNativeView.h"
|
||||
|
||||
@implementation FLPlugin
|
||||
|
||||
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
|
||||
FLNativeViewFactory* factory =
|
||||
[[FLNativeViewFactory alloc] initWithMessenger:registrar.messenger];
|
||||
[registrar registerViewFactory:factory withId:@"<platform-view-type>"];
|
||||
}
|
||||
|
||||
@end
|
||||
```
|
||||
|
||||
For more information, see the API docs for:
|
||||
|
||||
* [`FlutterPlatformViewFactory`](https://api.flutter.dev/objcdoc/Protocols/FlutterPlatformViewFactory.html)
|
||||
* [`FlutterPlatformView`](https://api.flutter.dev/objcdoc/Protocols/FlutterPlatformView.html)
|
||||
* [`PlatformView`](https://api.flutter.dev/javadoc/io/flutter/plugin/platform/PlatformView.html)
|
||||
|
||||
By default, the `UIKitView` widget appends the native `UIView` to the view hierarchy. For more documentation, see [UIKitView](https://api.flutter.dev/flutter/widgets/UiKitView-class.html).
|
||||
|
||||
## Performance
|
||||
|
||||
Platform views in Flutter come with performance trade-offs.
|
||||
|
||||
For example, in a typical Flutter app, the Flutter UI is composed
|
||||
on a dedicated raster thread. This allows Flutter apps to be fast,
|
||||
as the main platform thread is rarely blocked.
|
||||
|
||||
While a platform view is rendered with Hybrid Composition, the Flutter UI is composed from
|
||||
the platform thread, which competes with other tasks like
|
||||
handling OS or plugin messages, etc.
|
||||
|
||||
Prior to Android 10, Hybrid Composition copies each Flutter frame
|
||||
out of the graphic memory into main memory and then copied back to
|
||||
a GPU texture. As this copy happens per frame, the performance of
|
||||
the entire Flutter UI may be impacted.
|
||||
|
||||
On the other hand, Virtual Display makes each pixel of the native view
|
||||
flow through additional intermediate graphic buffers, which cost graphic
|
||||
memory and drawing performance.
|
||||
|
||||
For complex cases, there are some techniques that can be used to mitigate
|
||||
these issues.
|
||||
|
||||
For example, you could use a placeholder texture while an animation is
|
||||
happening in Dart. In other words, if an animation is slow while a
|
||||
platform view is rendered, then consider taking a screenshot of the
|
||||
native view and rendering it as a texture.
|
||||
|
||||
For more information, see:
|
||||
|
||||
* [`TextureLayer`](https://api.flutter.dev/flutter/rendering/TextureLayer-class.html)
|
||||
* [`TextureRegistry`](https://api.flutter.dev/javadoc/io/flutter/view/TextureRegistry.html)
|
||||
* [`FlutterTextureRegistry`](https://api.flutter.dev/objcdoc/Protocols/FlutterTextureRegistry.html)
|
||||
*For information on using platform views in a Flutter app, see the documentation for
|
||||
[Android](https://docs.flutter.dev/platform-integration/android/platform-views),
|
||||
[iOS](https://docs.flutter.dev/platform-integration/ios/platform-views), and
|
||||
[macOS](https://docs.flutter.dev/platform-integration/macos/platform-views).*
|
||||
|
||||
## Background
|
||||
|
||||
Hybrid Composition (HC) refers to a method of composing native views (for example, a native webview) alongside Flutter widgets.
|
||||
On Android, it is one of several modes for displaying platform views; see [Android Platform Views](Android-Platform-Views.md) for an overview of modes.
|
||||
On iOS and macOS, it is the only mode used for displaying platform views.
|
||||
|
||||
## Approach
|
||||
|
||||
HC creates multiple layers of native views that are composited by the standard platform
|
||||
UI toolkit rather than by Flutter. This requires separating the Flutter rendering into
|
||||
separate views, one containing the things that are behind the native view, and another
|
||||
things above the native view, so that everything looks correct when composited by the
|
||||
system.
|
||||
|
||||
Because it involves coordinating multiple native views, it adds complexity to the
|
||||
rendering pipeline (requiring synchronization between OS rendering and Flutter
|
||||
rendering to avoid tearing) and event handling such as gesture resolution.
|
||||
|
||||
## Limitations
|
||||
|
||||
- **Thread performance.** Normally, the Flutter UI is composed
|
||||
on a dedicated raster thread. This allows Flutter apps to be fast,
|
||||
as the main platform thread is rarely blocked. While a platform view
|
||||
is rendered with Hybrid Composition, the Flutter UI is composed from
|
||||
the platform thread, which competes with other tasks like
|
||||
handling OS or plugin messages.
|
||||
- **Gesture handling.** Coordinating gesture resolution between Flutter's
|
||||
gesture arena and the native gesture system sometimes results in
|
||||
unexpected behaviors, such as specific gestures not working correctly
|
||||
over native views.
|
||||
|
||||
### Android
|
||||
|
||||
- Prior to Android 10 (API 29), Hybrid Composition copies each Flutter frame
|
||||
out of the graphic memory into main memory and then copied back to
|
||||
a GPU texture. As this copy happens per frame, the performance of
|
||||
the entire Flutter UI may be impacted.
|
||||
|
||||
To see all known issues specific to this mode on Android, search for the [`platform-views: hc` label](https://github.com/flutter/flutter/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22platform-views%3A%20hc%22).
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user