From 01d37bc80fd5bb9b980b63b64c5d322038ac41e5 Mon Sep 17 00:00:00 2001 From: stuartmorgan-g Date: Wed, 7 Jan 2026 12:10:47 -0500 Subject: [PATCH] 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> --- docs/platforms/Hybrid-Composition.md | 438 +++------------------------ 1 file changed, 44 insertions(+), 394 deletions(-) diff --git a/docs/platforms/Hybrid-Composition.md b/docs/platforms/Hybrid-Composition.md index 4044dab4742..4fb3ed9c294 100644 --- a/docs/platforms/Hybrid-Composition.md +++ b/docs/platforms/Hybrid-Composition.md @@ -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 creationParams = {}; - - return PlatformViewLink( - viewType: viewType, - surfaceFactory: - (BuildContext context, PlatformViewController controller) { - return AndroidViewSurface( - controller: controller, - gestureRecognizers: const >{}, - 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 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 creationParams = (Map) 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("", 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("", 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: - - -```dart -import 'package:flutter/widget.dart'; -``` - - -2. Implement a `build()` method: - - -```dart -Widget build(BuildContext context) { - // This is used in the platform side to register the view. - final String viewType = ''; - // Pass parameters to the platform side. - final Map creationParams = {}; - - 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 - -@interface FLNativeViewFactory : NSObject -- (instancetype)initWithMessenger:(NSObject*)messenger; -@end - -@interface FLNativeView : NSObject - -- (instancetype)initWithFrame:(CGRect)frame - viewIdentifier:(int64_t)viewId - arguments:(id _Nullable)args - binaryMessenger:(NSObject*)messenger; - -- (UIView*)view; -@end -``` - -Implement the factory and the platform view in `FLNativeView.m` - -```objc -#import "FLNativeView.h" - -@implementation FLNativeViewFactory { - NSObject* _messenger; -} - -- (instancetype)initWithMessenger:(NSObject*)messenger { - self = [super init]; - if (self) { - _messenger = messenger; - } - return self; -} - -- (NSObject*)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*)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* registrar = - [self registrarForPlugin:@"plugin-name"]; - - FLNativeViewFactory* factory = - [[FLNativeViewFactory alloc] initWithMessenger:registrar.messenger]; - [[self registrarForPlugin:@""] registerViewFactory:factory - withId:@""]; - 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*)registrar { - FLNativeViewFactory* factory = - [[FLNativeViewFactory alloc] initWithMessenger:registrar.messenger]; - [registrar registerViewFactory:factory withId:@""]; -} - -@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).