在 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,请执行下列操作:

  1. 添加下面的导入代码:

    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';
  2. 实现一个 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,请执行下列操作:

  1. 添加下面的导入代码:

    import 'package:flutter/material.dart';
    import 'package:flutter/services.dart';
  2. 实现一个 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 中发生动画时,您可以使用占位符纹理。换句话说,如果在渲染平台视图时动画很慢,请考虑对原生视图进行截图,并将其渲染为纹理。

更多信息,请查看下面链接: