How a Flutter component works

For a very detailed overview of Flutter architecture on all platforms, see this doc.

A Flutter component for Fuchsia is an ELF binary that is built and run using Fuchsia's Bazel SDK. The component is launched by Fuchsia's ELF runner (there is no longer a separate Flutter runner or Dart runner).

Building and running an example Flutter component is done by:

  1. Compiling the Flutter application to get a folder of compiled assets (snapshot, fonts, images).

    This is currently done with the Flutter CLI using flutter build bundle. This only supports JIT compilation. To support AOT compilation, we should switch to using the Dart SDK's tools instead (see this doc for reference).

  2. Packaging these assets together with the embedder executable and shared libraries. See “Package layout” for details.

  3. Running the embedder executable with the ELF runner. This is handled by the Bazel SDK.

    We pass the path to the compiled assets as an argument here.

Running the application using the assets is handled by libflutter_engine.so, a shared library from the Flutter Engine repository. We interact with libflutter_engine.so using embedder.h.

embedder architecture diagram

Package layout

The compiled Flutter component's package has three main folders:

  • bin/embedder: The executable.

  • data/flutter_assets/: The compiled assets of the Flutter app that should be run by the executable.

  • lib/: Shared libraries that are used by bin/embedder.

    Notably, lib/libflutter_engine.so is the Flutter Engine code that bin/embedder uses to run the Flutter app.

When building and running a Flutter example, the full package contents are printed for debugging.

Threading model

The easiest way to understand how threading works in Flutter is to attach a debugger in VS Code and break on a function that you're interested in. VS Code will tell you what thread the code is running on.

threads in VS Code

Some notable threads:

  • Platform thread (initial-thread): The thread that main() runs on.

    • We connect to all FIDL services on this thread and platform messages are sent on this thread.

    • Async requests and responses to FIDL services are handled by an async loop that is started by FlutterEngineRun.

  • Raster thread (io.flutter.raster): Handles rendering logic for Flutter.

    • Callbacks for rendering are called on this thread.