平台视图允许你在 Flutter 应用中嵌入原生视图,这样你就可以从 Dart 应用转换、裁剪和调整原生视图的不透明度。

例如,这允许您在 Flutter 应用中直接使用 Android SDK 中的原生 Google 地图。

Android 上的平台视图有两种实现方式。它们在性能和保真度方面都有权衡。平台视图需要 Android API 23+。

平台视图照常渲染。Flutter 内容渲染到纹理中。SurfaceFlinger 组合 Flutter 内容和平台视图。

  • + Android 视图的最佳性能和保真度。
  • - Flutter 性能下降。
  • - 应用程序的 FPS 会更低。
  • - 某些可应用于 Flutter 小部件的转换在应用于平台视图时将不起作用。

纹理层(或纹理层混合合成)

#

平台视图渲染到纹理中。Flutter 绘制平台视图(通过纹理)。Flutter 内容直接渲染到 Surface 中。

  • + Android 视图的良好性能
  • + Flutter 渲染的最佳性能。
  • + 所有转换均正常工作。
  • - 快速滚动(例如 Web 视图)会卡顿
  • - SurfaceView 在此模式下存在问题,将被移动到虚拟显示器中(破坏无障碍功能)
  • - 除非 Flutter 渲染到 TextureView 中,否则文本放大器将失效。

要在 Android 上创建平台视图,请使用以下步骤

在 Dart 侧

#

在 Dart 侧,创建一个 Widget 并添加以下构建实现之一。

混合合成

#

在您的 Dart 文件中,例如 native_view_example.dart,请使用以下说明

  1. 添加以下导入:

    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';
  2. 实现 build() 方法

    dart
    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 文档:

TextureLayerHybridComposition

#

在您的 Dart 文件中,例如 native_view_example.dart,请使用以下说明

  1. 添加以下导入:

    dart
    import 'package:flutter/material.dart';
    import 'package:flutter/services.dart';
  2. 实现 build() 方法

    dart
    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 文档:

在平台侧

#

在平台侧,在 Kotlin 或 Java 中使用标准 io.flutter.plugin.platform

在您的原生代码中,实现以下内容

扩展 io.flutter.plugin.platform.PlatformView 以提供对 android.view.View 的引用(例如,NativeView.kt

kotlin
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

kotlin
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

kotlin
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

kotlin
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

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

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

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

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 版本

kotlin
android {
    defaultConfig {
        minSdk = 19 // if using hybrid composition
        minSdk = 20 // if using virtual display.
    }
}

SurfaceView

#

处理 SurfaceView 对 Flutter 来说是个问题,应尽可能避免。

手动视图失效

#

某些 Android 视图在内容更改时不会自动失效。一些示例视图包括 SurfaceViewSurfaceTexture。当您的平台视图包含这些视图时,您需要在它们被绘制后(或者更具体地说:在交换链翻转后)手动使视图失效。手动视图失效是通过对 View 或其父视图之一调用 invalidate 来完成的。

问题

#

现有平台视图问题

性能

#

Flutter 中的平台视图会带来性能上的权衡。

例如,在典型的 Flutter 应用中,Flutter UI 在专用的光栅线程上合成。这使得 Flutter 应用能够快速运行,因为主平台线程很少被阻塞。

当平台视图使用混合合成渲染时,Flutter UI 是从平台线程合成的,这与处理操作系统或插件消息等其他任务竞争。

在 Android 10 之前,混合合成将每个 Flutter 帧从图形内存复制到主内存,然后再复制回 GPU 纹理。由于这种复制是按帧进行的,因此整个 Flutter UI 的性能可能会受到影响。在 Android 10 或更高版本中,图形内存只复制一次。

另一方面,虚拟显示器使原生视图的每个像素流经额外的中间图形缓冲区,这会消耗图形内存和绘图性能。

对于复杂情况,可以使用一些技术来缓解这些问题。

例如,您可以在 Dart 中发生动画时使用占位符纹理。换句话说,如果平台视图渲染时动画很慢,那么可以考虑截取原生视图的屏幕截图并将其渲染为纹理。

有关更多信息,请参阅