Background

Flutter UIs function conceptually similar to a WebView on Android. The Flutter Framework takes a widget tree described by the app developer and translates that into an internal widget hierarchy, and then from that decides what pixels to actually render. Web developers can think of this as analogous to how a browser takes HTML/CSS, creates a Document Object Model (“DOM”), and then uses that DOM to actually render pixels each frame. Also like a WebView, Flutter UIs aren't translated to a set of Android View widgets and composited by the Android OS itself. Flutter controls a texture (generally through a SurfaceView) and uses Skia to render directly to the texture without ever using any kind of Android View hierarchy to represent its internal Flutter widget hierarchy or UI.

This means that like a WebView, by default a Flutter UI could never contain an Android View within its widget hierarchy. Since the Flutter UI is just being drawn to a texture and its widget tree is entirely internal, there‘s no place for the View to “fit” within Flutter’s internal model or render interleaved within Flutter widgets. That's a problem for developers that would like to include complex pre-existing Android views in their Flutter apps, like WebViews themselves, or maps.

To solve this problem Flutter created an AndroidView widget that Flutter developers can use to visually embed actual Android View components within their Flutter UI.

Approaches

There are currently four different implementations of Android platform views:

Each has a different set of limitations and tradeoffs, as discussed below. The pages linked above give details about each implementation.

Hybrid Composition++

This mode is the latest hybrid composition strategy, and is 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.

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.

Opting In

Because HCPP acts as a global upgrade for how platform views are backed, it is enabled via configuration rather than standard Dart initialization methods (initAndroidView, etc.).

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
    

    Note: This flag is intended for local execution and testing. It cannot be passed to flutter build commands. For release builds, use the manifest configuration below.

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

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

Limitations and Known Issues

The following is a list of limitations and known issues. If you encounter an issue not listed below, please file an issue!

  • SurfaceView Compatibility: Opting in is currently not recommended if your application contains a platform view which is or contains a native SurfaceView (often used by video players or map plugins), due to clipping issues. This is tracked in https://github.com/flutter/flutter/issues/175546.
  • Complex Overlay Stacking: Transparent platform views will not display correctly in layout stacks structured as: Flutter canvas -> Platform View -> Overlay -> Transparent Platform View, when all four of these layers intersect.

Virtual Display

This mode works by rendering the platform view into a VirtualDisplay, whose contents are connected to a Flutter Texture.

Because this renders to a Texture, it integrates well into the Flutter drawing system. However, the use of VirtualDisplay introduces a number of compatibility issues, including with text input, accessibility, and secondary views (see the Virtual Display page for details).

This display mode requires SDK 20 or later.

Hybrid Composition

This mode directly displays the native Android View in the view hierarchy. This requires several significant changes to the way Flutter renders:

  • The Flutter widget tree is divided into two different Android Views, one below the platform view and one above it.
  • To avoid tearing or other visual artifacts, Flutter's composition must be done on the platform thread rather than a dedicated thread.

Because the native view is being displayed directly, just as it would be in non-Flutter application, this mode is the least likely to have compatibility issues with the platform view. However, the rendering changes can significantly impact Flutter's performance. In addition, on versions of Android before SDK 29 (Android 10) Flutter frames have a GPU->CPU->GPU round trip that further impacts performance.

This display mode requires SDK 19 or later, and uses the FlutterImageView based renderer.

Texture Layer Hybrid Composition

This mode, introduced in Flutter 3.0, attempted to address the limitations of the modes above, and was originally intended to replace both. Like Hybrid Composition, the view is actually placed on the screen at the correct location. However, as with Virtual Display the drawing uses a Texture, which in this case is populated by redirecting draw.

In most cases this combines the best aspects of Virtual Display and Hybrid Composition, and should be preferred when possible. One notable exception however is that if the platform view is, or contains, a SurfaceView this mode will not work correctly, and the SurfaceView will be drawn at the wrong location and/or z-index.

This display mode requires SDK 23 or later.

Selecting a mode

Usually Android platform views are created with one of the init* methods:

  • initAndroidView creates a TextureAndroidViewController, which will use TLHC mode if possible, and fall back to VD if not. The fallback is triggered if:

    • the current SDK version is <23, or
    • the platform view hierarchy contains a SurfaceView (or subclass) at creation time.

    There is a known issue where if the view hierarchy does not contain a SurfaceView at creation time, but one is added later, rendering will not work correctly. Until that issue is resolved, plugin authors can work around this by either:

    • including a 0x0 SurfaceView in the view hierarchy at creation time, to trigger fallback to VD, or
    • switching to initExpensiveAndroidView to require HC.

    (The behavior described above is for Flutter 3.3+. Flutter 3.0 did not include the fallback to VD, and in Flutter <3.0 this always used VD.)

  • initSurfaceAndroidView creates a SurfaceAndroidViewController, which will use TLHC if possible, and fall back to HC if not. As above, the fallback is triggered if:

    • the current SDK version is <23, or
    • the platform view hierarchy contains a SurfaceView (or subclass) at creation time.

    There is a known issue where if the view hierarchy does not contain a SurfaceView at creation time, but one is added later, rendering will not work correctly. Until that issue is resolved, plugin authors can work around this by either:

    • including a 0x0 SurfaceView in the view hierarchy at creation time, to trigger fallback to HC, or
    • switching to initExpensiveAndroidView to require HC directly.

    (The behavior described above is for Flutter 3.7+. In Flutter 3.0 and 3.0 this behaved identically to initAndroidView, and in Flutter <3.0 this always used HC.)

  • initExpensiveAndroidView creates an ExpensiveAndroidViewController, which will always use HC.

    (This API was introduced in Flutter 3.0.)

In general, plugins will likely get the best performance by using initAndroidView or initSurfaceAndroidView (depending on which fallback mode is desired for older versions of Android), and should only use initExpensiveAndroidView if the plugin is found to be incompatible with TLHC (such as dynamically adding a SurfaceView).