mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00

This is waiting on - https://github.com/flutter/flutter/pull/148777 - https://github.com/flutter/flutter/pull/148790 After this PR lands, there will likely be 1-2 more clean up PRs, after which the migration will be done! --- This moves the remaining wiki pages as planned in [flutter.dev/go/migrate-flutter-wiki-spreadsheet](https://docs.google.com/spreadsheets/d/1x65189ZBdNiLRygpUYoU08pwvXD4M-Z157c6pm8deGI/edit?usp=sharing) It also adds the team labels to the label bot for future PRs. Changes to the content were only updating cross links, or links to refer to the main branch rather than master. Remaining links to the wiki will be updated once all other pages have finished moving, they still work in the meantime. Part of https://github.com/flutter/flutter/issues/145009
395 lines
12 KiB
Markdown
395 lines
12 KiB
Markdown
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)
|