在 Flutter 应用中使用 Platform Views 托管原生 Android 视图
平台视图允许你在 Flutter 应用中嵌入原生视图,这样你就可以从 Dart 应用转换、裁剪和调整原生视图的不透明度。
例如,这允许您直接在 Flutter 应用中使用 Android SDK 中的原生 Google 地图。
Android 上的 Platform Views 有两种实现方式。它们在性能和保真度方面都有权衡。Platform views 要求 Android API 23+。
Platform Views 按正常方式渲染。Flutter 内容被渲染到一个纹理中。SurfaceFlinger 会组合 Flutter 内容和平台视图。
+
最佳的 Android 视图性能和保真度。-
Flutter 性能会受影响。-
应用的 FPS 会较低。-
应用于 Flutter 控件的某些变换将无法应用于平台视图。
Platform Views 被渲染成一个纹理。Flutter 通过纹理绘制平台视图。Flutter 内容直接被渲染到一个 Surface 中。
+
Android 视图的良好性能。+
Flutter 渲染的最佳性能。+
所有变换都能正确工作。-
快速滚动(例如网页视图)会卡顿。-
SurfaceViews 在此模式下存在问题,将被移到一个虚拟显示器中(破坏可访问性)。-
文本放大镜会失效,除非 Flutter 被渲染到一个 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) {}
}
</string?,></string?,>
在您的原生代码中,实现以下内容:
继承 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) {}
}
</string,></string,></string,>
有关更多信息,请参阅 API 文档:
最后,修改您的 build.gradle
文件,以要求其中一个最低 Android SDK 版本。
android {
defaultConfig {
minSdk = 19 // if using hybrid composition
minSdk = 20 // if using virtual display.
}
}
Surface Views
#处理 SurfaceViews 对于 Flutter 来说很麻烦,应尽量避免。
手动视图无效化
#某些 Android 视图在内容更改时不会自行无效化。一些示例视图包括 SurfaceView
和 SurfaceTexture
。当您的平台视图包含这些视图时,您需要在它们被绘制之后(或者更具体地说:交换链翻转之后)手动无效化视图。手动视图无效化是通过调用视图或其父视图之一的 invalidate
来完成的。
问题
#性能
#Flutter 中的平台视图会带来性能上的权衡。
例如,在典型的 Flutter 应用中,Flutter UI 在一个专用的栅格线程上组合。这使得 Flutter 应用能够快速运行,因为主平台线程很少被阻塞。
在平台视图使用混合组合进行渲染时,Flutter UI 是从平台线程组合的,该线程需要与其他任务(如处理 OS 或插件消息)竞争。
在 Android 10 之前,混合组合会将每个 Flutter 帧从图形内存复制到主内存,然后再复制回 GPU 纹理。由于此复制操作发生在每一帧,因此可能会影响整个 Flutter UI 的性能。在 Android 10 及更高版本中,图形内存仅复制一次。
另一方面,虚拟显示器使得原生视图的每个像素都通过额外的中间图形缓冲区流动,这会消耗图形内存和绘制性能。
对于复杂情况,有一些技术可以用来缓解这些问题。
例如,您可以在 Dart 中进行动画时使用占位符纹理。换句话说,如果平台视图正在渲染时动画速度很慢,则可以考虑截取原生视图的屏幕截图并将其渲染为纹理。
有关更多信息,请参阅: