在 Flutter 應用中使用整合平臺視圖託管您的原生 Android 檢視
整合平臺視圖(後稱為平臺視圖)允許將原生檢視嵌入到 Flutter 應用中,所以您可以透過 Dart 將變換、裁剪和不透明度等效果應用到原生檢視。
例如,這使您可以透過使用平臺視圖直接在 Flutter 應用內部使用 Android 和 iOS SDK 中的 Google Maps。
Flutter 支援兩種整合模式:虛擬顯示模式 (Virtual displays) 和混合整合模式 (Hybrid composition) 。
我們應根據具體情況來決定使用哪種模式。讓我們來看看:
-
混合整合模式 會將原生的
android.view.View
附加到檢視層次結構中。因此,鍵盤處理和無障礙功能是開箱即用的。在 Android 10 之前,此模式可能會大大降低 Flutter UI 的幀吞吐量 (FPS)。有關更多資訊,請參見 效能 小節。 -
虛擬顯示模式 會將
android.view.View
例項渲染為紋理,因此它不會嵌入到 Android Activity 的檢視層次結構中。某些平台互動(例如鍵盤處理和輔助功能)可能無法正常工作。
在 Android 上建立平臺視圖需要如下的步驟:
在 Dart 中進行的處理
在 Dart 端,建立一個 Widget
然後新增如下的實現,具體如下:
混合整合模式
在 Dart 檔案中,例如 native_view_example.dart
,請執行下列操作:
-
新增下面的匯入程式碼:
import '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()
方法:Widget 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 文件:
虛擬顯示模式 (Virtual Display)
在 Dart 檔案中,例如 native_view_example.dart
,請執行下列操作:
-
新增下面的匯入程式碼:
import 'package:flutter/material.dart'; import 'package:flutter/services.dart';
-
實現一個
build()
方法:Widget 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 文件:
在平臺端
在平臺端,使用 Java 或 Kotlin 中的標準包
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 android.view.View
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) {
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 {
minSdkVersion 19 // if using hybrid composition
minSdkVersion 20 // if using virtual display.
}
}
效能
在 Flutter 中使用平臺視圖時,效能會有所取捨。
例如,在典型的 Flutter 應用中,Flutter 的 UI 是專門在 raster 執行緒上合成的。由於平台的主執行緒很少被阻塞,因此 Flutter 應用程式可以快速執行。
使用混合整合模式渲染平臺視圖時, Flutter UI 由平台執行緒完成,與其他執行緒一起競爭,例如:處理系統或外掛訊息等任務。
在 Android 10 之前,混合整合模式將每個 Flutter 幀從視訊記憶體中複製到主記憶體中,然後再將其複製回 GPU 紋理中。在 Android 10 或更高版本中,視訊記憶體會被複制兩次。由於每幀都會進行一次複製,因此可能會影響整個 Flutter UI 的效能。
另一方面,虛擬顯示模式使平臺視圖的每個畫素流經附加的中間圖形緩衝區,這會浪費視訊記憶體和繪圖效能。
對於複雜的情況,可以使用一些技巧來緩解這些問題。
例如,當 Dart 中發生動畫時,您可以使用佔位符紋理。換句話說,如果在渲染平臺視圖時動畫很慢,請考慮對原生檢視進行截圖,並將其渲染為紋理。
更多資訊,請檢視下面連結: