在 Flutter 应用中使用集成平台视图托管你的原生 Android 视图
集成平台视图(后称为平台视图)允许将原生视图嵌入到 Flutter 应用中,所以你可以通过 Dart 将变换、裁剪和不透明度等效果应用到原生视图。
例如,这使你可以通过使用平台视图直接在 Flutter 应用内部使用 Android 和 iOS SDK 中的 Google Maps。
Platform Views on Android have two implementations. They come with tradeoffs both in terms of performance and fidelity. Platform views require Android API 23+.
Platform Views are rendered as they are normally. Flutter content is rendered into a texture. SurfaceFlinger composes the Flutter content and the platform views.
+
best performance and fidelity of Android views.-
Flutter performance suffers.-
FPS of application will be lower.-
Certain transformations that can be applied to Flutter widgets will not work when applied to platform views.
Texture Layer (or Texture Layer Hybrid Composition)
#Platform Views are rendered into a texture. Flutter draws the platform views (via the texture). Flutter content is rendered directly into a Surface.
+
good performance for Android Views+
best performance for Flutter rendering.+
all transformations work correctly.-
quick scrolling (e.g. a web view) will be janky-
SurfaceViews are problematic in this mode and will be moved into a virtual display (breaking a11y)-
Text magnifier will break unless Flutter is rendered into a TextureView.
在 Android 上创建平台视图需要如下的步骤:
在 Dart 中进行的处理
#在 Dart 端,创建一个 Widget
然后添加如下的实现,具体如下:
混合集成模式
#在 Dart 文件中,例如 native_view_example.dart
,请执行下列操作:
-
添加下面的导入代码:
dartimport 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart';
-
实现一个
build()
方法:dartWidget build(BuildContext context) { // This is used in the platform side to register the view. const String viewType = '<platform-view-type>'; // Pass parameters to the platform side. const Map<String, dynamic> creationParams = <String, dynamic>{}; return PlatformViewLink( viewType: viewType, surfaceFactory: (context, controller) { return AndroidViewSurface( controller: controller as AndroidViewController, gestureRecognizers: const <Factory<OneSequenceGestureRecognizer>>{}, hitTestBehavior: PlatformViewHitTestBehavior.opaque, ); }, onCreatePlatformView: (params) { return PlatformViewsService.initSurfaceAndroidView( id: params.id, viewType: viewType, layoutDirection: TextDirection.ltr, creationParams: creationParams, creationParamsCodec: const StandardMessageCodec(), onFocus: () { params.onFocusChanged(true); }, ) ..addOnPlatformViewCreatedListener(params.onPlatformViewCreated) ..create(); }, ); }
更多信息,查阅 API 文档:
TextureLayerHybridComposition
#在 Dart 文件中,例如 native_view_example.dart
,请执行下列操作:
-
添加下面的导入代码:
dartimport 'package:flutter/material.dart'; import 'package:flutter/services.dart';
-
实现一个
build()
方法:dartWidget build(BuildContext context) { // This is used in the platform side to register the view. const String viewType = '<platform-view-type>'; // Pass parameters to the platform side. final Map<String, dynamic> creationParams = <String, dynamic>{}; return AndroidView( viewType: viewType, layoutDirection: TextDirection.ltr, creationParams: creationParams, creationParamsCodec: const StandardMessageCodec(), ); }
更多信息,查阅 API 文档:
在平台端
#在平台端,使用 Kotlin 或 Java 中的标准包
io.flutter.plugin.platform
:
在你的原生代码中,实现如下方法:
继承 io.flutter.plugin.platform.PlatformView
以提供对 android.view.View
的引用,如 NativeView.kt
所示:
package dev.flutter.example
import android.content.Context
import android.graphics.Color
import android.view.View
import android.widget.TextView
import io.flutter.plugin.platform.PlatformView
internal class NativeView(context: Context, id: Int, creationParams: Map<String?, Any?>?) : PlatformView {
private val textView: TextView
override fun getView(): View {
return textView
}
override fun dispose() {}
init {
textView = TextView(context)
textView.textSize = 72f
textView.setBackgroundColor(Color.rgb(255, 255, 255))
textView.text = "Rendered on a native Android view (id: $id)"
}
}
创建一个用来创建 NativeView
的实例的工厂类,参考 NativeViewFactory.kt
:
package dev.flutter.example
import android.content.Context
import io.flutter.plugin.common.StandardMessageCodec
import io.flutter.plugin.platform.PlatformView
import io.flutter.plugin.platform.PlatformViewFactory
class NativeViewFactory : PlatformViewFactory(StandardMessageCodec.INSTANCE) {
override fun create(context: Context, viewId: Int, args: Any?): PlatformView {
val creationParams = args as Map<String?, Any?>?
return NativeView(context, viewId, creationParams)
}
}
最后,注册这个平台视图。这一步可以在应用中,也可以在插件中。
要在应用中进行注册,修改应用的主 Activity
(例如:MainActivity.kt
):
package dev.flutter.example
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
class MainActivity : FlutterActivity() {
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
flutterEngine
.platformViewsController
.registry
.registerViewFactory("<platform-view-type>",
NativeViewFactory())
}
}
要在插件中进行注册,修改你插件的主类
(例如:PlatformViewPlugin.kt
):
package dev.flutter.plugin.example
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.FlutterPlugin.FlutterPluginBinding
class PlatformViewPlugin : FlutterPlugin {
override fun onAttachedToEngine(binding: FlutterPluginBinding) {
binding
.platformViewRegistry
.registerViewFactory("<platform-view-type>", NativeViewFactory())
}
override fun onDetachedFromEngine(binding: FlutterPluginBinding) {}
}
在你的原生代码中,实现如下方法:
继承 io.flutter.plugin.platform.PlatformView
以提供对 android.view.View
的引用,如 NativeView.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;
import java.util.Map;
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() {}
}
创建一个用来创建 NativeView
的实例的工厂类,参考 NativeViewFactory.java
:
package dev.flutter.example;
import android.content.Context;
import androidx.annotation.Nullable;
import androidx.annotation.NonNull;
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 {
NativeViewFactory() {
super(StandardMessageCodec.INSTANCE);
}
@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);
}
}
最后,注册这个平台视图。这一步可以在应用中,也可以在插件中。
要在应用中进行注册,修改应用的主 Activity
(例如:MainActivity.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());
}
}
要在插件中进行注册,修改插件的主类 (例如:PlatformViewPlugin.java
):
package dev.flutter.plugin.example;
import androidx.annotation.NonNull;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
public class PlatformViewPlugin implements FlutterPlugin {
@Override
public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
binding
.getPlatformViewRegistry()
.registerViewFactory("<platform-view-type>", new NativeViewFactory());
}
@Override
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {}
}
更多信息,请查看 API 文档:
最后,修改你的 build.gradle
文件来满足 Android SDK 最低版本的要求:
android {
defaultConfig {
minSdk = 19 // if using hybrid composition
minSdk = 20 // if using virtual display.
}
}
Surface Views
#Handling SurfaceViews is problematic for Flutter and should be avoided when possible.
Manual view invalidation
#Certain Android Views do not invalidate themselves when their content changes.
Some example views include SurfaceView
and SurfaceTexture
.
When your Platform View includes these views you are required to
manually invalidate the view after they have been drawn to
(or more specifically: after the swap chain is flipped).
Manual view invalidation is done by calling invalidate
on the View
or one of its parent views.
Issues
#性能
#在 Flutter 中使用平台视图时,性能会有所取舍。
例如,在典型的 Flutter 应用中,Flutter 的 UI 是专门在 raster 线程上合成的。由于平台的主线程很少被阻塞,因此 Flutter 应用程序可以快速运行。
使用混合集成模式渲染平台视图时, Flutter UI 由平台线程完成,与其他线程一起竞争,例如:处理系统或插件消息等任务。
在 Android 10 之前,混合集成模式将每个 Flutter 帧从显存中复制到主内存中,然后再将其复制回 GPU 纹理中。在 Android 10 或更高版本中,显存会被复制两次。由于每帧都会进行一次复制,因此可能会影响整个 Flutter UI 的性能。
另一方面,虚拟显示模式使平台视图的每个像素流经附加的中间图形缓冲区,这会浪费显存和绘图性能。
对于复杂的情况,可以使用一些技巧来缓解这些问题。
例如,当 Dart 中发生动画时,你可以使用占位符纹理。换句话说,如果在渲染平台视图时动画很慢,请考虑对原生视图进行截图,并将其渲染为纹理。
更多信息,请查看下面链接:
除非另有说明,本文档之所提及适用于 Flutter 的最新稳定版本,本页面最后更新时间: 2024-11-02。 查看文档源码 或者 为本页面内容提出建议。