Appearance
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 three different implementations of Android platform views:
- Virtual Display (VD)
- Hybrid Composition (HC)
- Texture Layer Hybrid Composition (TLHC)
Each has a different set of limitations and tradeoffs, as discussed below. The pages linked above give details about each implementation.
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
View
s, 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 aTextureAndroidViewController
, 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 ❤️.0 this always used VD.)
initSurfaceAndroidView
creates aSurfaceAndroidViewController
, 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 ❤️.0 this always used HC.)initExpensiveAndroidView
creates anExpensiveAndroidViewController
, 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
).