跳转至正文

在 Flutter 应用中使用集成平台视图托管你的原生 Android 视图

学习如何在 Flutter 应用中使用集成平台视图托管你的原生 Android 视图。

集成平台视图(后称为平台视图)允许将原生视图嵌入到 Flutter 应用中,所以你可以通过 Dart 将变换、裁剪和不透明度等效果应用到原生视图。

例如,这使你可以通过使用平台视图直接在 Flutter 应用内部使用 Android 和 iOS SDK 中的 Google Maps。

Android 平台上的视图有多种实现方式。这些实现方式在性能和保真度方面都存在取舍。

Choosing an implementation

#

The following matrix summarizes the different implementations and their trade-offs:

Mode Benefits Considerations Enabler
Texture layer • Good Flutter performance
• Full widget transforms work
• Janky during quick scrolling
• SurfaceViews lose accessibility and text magnifier breaks
Default behavior or standard AndroidView
Hybrid composition • Full native fidelity
• Correct accessibility and SurfaceView support
• Causes thread merging of raster & platform, which degrades Flutter FPS
• Platform View -> Renders to texture -> Uploads to Impeller -> Impeller composites Flutter content and Platform View content
PlatformViewLink with AndroidViewSurface
AndroidViewController builds either a TLHC or an HC Platform View
HCPP (Experimental) • Full fidelity and performance
• Solves original sync overhead
• Requires Android API 34+, Vulkan support, and use of the Impeller rendering engine
• Platform View -> Renders to native Android Surface, Impeller renders to native Android Surface, SurfaceFlinger composites the two together
<meta-data> in AndroidManifest.xml
--enable-hcpp local flag
AndroidViewController builds either a TLHC or an HC Platform View

Hybrid composition

#

Platform Views are rendered as they are normally. Flutter content is rendered into a texture. SurfaceFlinger composes the Flutter content and the platform views.

Hybrid composition++ (HCPP)

#

HCPP is the latest hybrid composition strategy, designed to solve compositing performance and synchronization issues seen in the original Hybrid Composition mode. It is currently available as an opt-in feature.

Requirements

#
  • Android API 34 or later: Required for native transaction synchronization capabilities.
  • Vulkan rendering: The device must be capable of rendering with Vulkan. Required for Impeller to be enabled.

If these requirements are not met on the end-user device, Flutter will automatically fall back to the existing platform view strategy configured for the app.

Opt in

#

Because HCPP acts as a global upgrade for how platform views are backed, it's enabled through configuration rather than standard Dart initialization methods (initAndroidView, and so on).

You can enable HCPP using one of the following methods:

  1. Command line flag (run/test): Pass the --enable-hcpp flag to your flutter run or flutter test command:

    flutter run --enable-hcpp
    
  2. AndroidManifest.xml: Include a <meta-data> tag inside the <application> block of your AndroidManifest.xml:

    xml
    <meta-data
        android:name="io.flutter.embedding.android.EnableHcpp"
        android:value="true" />
    

Limitations and known issues

#
  • Complex overlay stacking: Transparent platform views won't display correctly in layout stacks structured as: Flutter canvas -> Platform View -> Overlay -> Transparent Platform View, when all four of these layers intersect.

Texture layer

#

Platform Views are rendered into a texture. Flutter draws the platform views (using the texture). Flutter content is rendered directly into a Surface.

This approach provides:

  • good performance for Android Views
  • good performance for Flutter rendering
  • all transformations work correctly

However, this approach might cause:

  • jankiness on quick scrolling (such as a web view)
  • broken accessibility for SurfaceViews
  • broken text magnification unless Flutter is rendered into a TextureView

在 Dart 中进行的处理

#

要在 Android 上创建平台视图,请按照以下步骤操作。首先,在 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(),
      );
    }
    

更多信息,请查看 AndroidView API 文档。

在平台端

#

在平台端,使用 Kotlin 或 Java 中的标准 package 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) {}
}

在你的原生代码中,实现如下方法:

继承 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) {}
}

更多信息,请查看 API 文档:

最后,修改你的 build.gradle 文件来满足 Android SDK 最低版本的要求:

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

Manual view invalidation

#

Certain Android Views don't invalidate themselves when their content changes. Some examples include SurfaceView and SurfaceTexture. When your Platform View includes these views, you must manually invalidate it after it has been drawn (or, more specifically, after the swap chain is flipped). Invalidate the view by calling invalidate on it or on one of its parents.

Issues

#

Check out the existing Platform View issues on GitHub.

Performance

#

Platform views in Flutter come with performance trade-offs.

In a typical Flutter app, the Flutter UI is composed on a dedicated raster thread, while platform code runs on the UI/platform thread. This separation keeps Flutter rendering fast and fluid.

However, when a platform view is rendered on Android using hybrid composition, Flutter merges the raster and UI threads into a single thread to ensure correct synchronization between the native Android views and the Flutter canvas. Because of this thread merging, rendering complex Flutter widgets alongside a platform view can compete with OS messages and plugin interactions, potentially causing lower application FPS and frame drops.

Also, prior to Android 10, hybrid composition copied each Flutter frame out of the graphic memory into main memory, and then copied it back to a GPU texture. As this copy happens per frame, the performance of the entire Flutter UI might be impacted. In Android 10 or above, the graphics memory is copied only once.

Hybrid Composition++ (HCPP) minimizes this overhead by using native transaction synchronization on supported devices (Android API 34+ with Vulkan), allowing superior performance without the heavy costs of original hybrid composition.

Virtual display, on the other hand, makes each pixel of the native view flow through additional intermediate graphic buffers, which cost graphic memory and drawing performance. This can cause jank during high-frequency updates like fast scrolling.

For complex cases, there are some techniques that can be used to mitigate these issues.

For example, you can use a placeholder texture while an animation is happening in Dart. In other words, if an animation is slow while a platform view is rendered, then consider taking a screenshot of the native view and rendering it as a texture.

For more information, visit the following API pages: