The problem background

What is Flutter for developers? What language is it written in, what parts are included, how is it compiled and run on the device? How does Flutter implement Hot Reload changes in Debug mode and Release mode natively? How does the Flutter project differ from our Android/iOS project, how does it relate to it, and how does it incorporate Android/iOS? How do Flutter’s rendering and event delivery mechanisms work? Does Flutter support hot update? Flutter does not officially provide ARMV7 support for iOS. Is this true? If engine bugs are found while using Flutter, how can they be corrected and implemented? How can build slowness or errors be located, modified, and implemented?

All this requires a holistic view of Flutter from its design, development and construction to its final operation.

This article will take a simple HELLO_flutter as an example to introduce the principle, customization and optimization of Flutter.

Introduction of Flutter

The Architecture of Flutter is divided into three layers :Framework, Engine, and Embedder.

Dart is used to implement the Framework, including Material Design style Widgets,Cupertino style Widgets (for iOS), basic text/image/button Widgets, rendering, animation, gestures, and more. The core code of this part is: Flutter package under the flutter repository, IO, Async, UI and other packages under the Sky_engine repository (DART: UI library provides the interface between the Flutter framework and the engine).

Engine is implemented in C++, including Skia,Dart, and Text. Skia is an open source two-dimensional graphics library that provides a common API for a variety of hardware and software platforms. It has been used as a graphics engine for Google Chrome, Chrome OS, Android, Mozilla Firefox, Firefox OS and many other products, Supported platforms also include Windows7+,macOS 10.10.5+,iOS8+,Android4.1+,Ubuntu14.04+, etc. The Dart section includes :Dart Runtime, Garbage Collection(GC), and JIT(Just In Time) support if In Debug mode. In Release and Profile mode, AOT(Ahead Of Time) compiles to native ARM code without JIT. Text is Text rendering, which is rendered at the following levels: derived from minikin’s libtxt library (for font selection and line separation); HartBuzz is used for glyphs selection and shaping; Skia works as a render /GPU back end, using FreeType rendering on Android and Fuchsia, and CoreGraphics for rendering fonts on iOS.

Embedder is an Embedder layer that allows the Flutter to be embedded to various platforms. The main tasks here include rendering Surface Settings, thread Settings, and plug-ins. From this we can see that the platform-dependent layer of Flutter is very low. Platforms (such as iOS) only provide a canvas, and all the remaining render related logic is inside Flutter, which makes it very cross-end consistent.

Flutter engineering structure

This article uses the development environment for Flutter Beta V0.3.1, corresponding to Engine Commit: 09D05A389.

Taking the Hello_FLUTTER project as an example, the structure of the Flutter project is as follows:

Ios is part of the ios code, using CocoaPods to manage dependencies, Android is part of the Android code, using Gradle to manage dependencies, lib is dart code, using pub to manage dependencies. Pubspec. yaml and pubspec.lock correspond to Cocoapods Podfile and podfile. lock in iOS.

Flutter model

For Flutter, it supports the usual debug, Release,profile and other modes, but it is different.

Debug mode: Indicates the Dart JIT mode, also called check mode or slow mode. Supports devices, emulators (iOS/Android), assertions are enabled in this mode, including all debugging information, service extensions, and debugging AIDS such as the Observatory. This pattern is optimized for rapid development and operation, but not for execution speed, package size, and deployment. In Debug mode, compilation uses JIT technology and supports the popular sub-second stateful Hot Reload.

Release mode: Corresponds to Dart’s AOT mode, which is deployed to end users. Only real machines are supported, not emulators. Turn off all assertions, remove as much debugging information as possible, and turn off all debugging tools. Package sizes are optimized for fast startup and execution. Disabled all debugging AIDS, service extensions.

Profile mode: Similar to Release mode, but with additional support for profile-mode service extensions, tracing support, and minimization of dependencies needed to use trace information, for example, observatory can be connected to processes. The reason the Profile does not support emulators is that diagnostics on emulators do not represent true performance.

Since there is no difference between Profile and Release in terms of compilation principles, this article only discusses Debug and Release modes.

In fact, the iOS/Android project under Flutter is still essentially a standard iOS/Android project, Flutter is simply generated and embedded in App. Framework and flutter. Framework (iOS) by adding a shell in BuildPhase. Gradle adds flutter NSTR (Android) to compile and embed Flutter related code in the native App. Therefore, this paper mainly discusses the construction, operation and other principles of flutter introduction. Although the compiler target includes ARM, X64,x86, and ARM64, this article will only discuss arm because of the similar principles (android defaults to ARMV7 unless otherwise specified).

Compilation and Running of Flutter code (iOS)

Compilation in Release mode

In Release mode, iOS project DART code construction link under Flutter is as follows:

Gen_snapshot is the DART compiler. It uses tree shaking(similar to tree logic, generating the smallest package, thus banning reflection features supported by DART in Flutter) and is responsible for generating assembly machine code. The final App. Framework is generated through a tool chain such as XCRun. All dart code, including business code, tripartite package code, and the flutter framework code they rely on, will eventually compile into app. framework.

PS. The tree shaking function in gensnapshot, corresponding to logic see: engine/SRC/thirdparty/dart/runtime/vm/compiler/aot/precompiler. Cc

The dart code eventually corresponds to the symbols in the App.framework as follows:

In fact, similar to the Android Release of product (see below), the App. The framework also includes kDartVmSnapshotData, kDartVmSnapshotInstructions, KDartIsolateSnapshotData kDartIsolateSnapshotInstructions four parts. Why does iOS use app. framework instead of Android’s four-file approach? The reason is that the Flutter engine cannot mark a memory page as executable on iOS due to system limitations, while Android can.

The Flutter. Framework corresponds to the engine part of the Flutter architecture, as well as Embedder. Practice Flutter. The framework is located in the/bin/cache/artifacts of Flutter warehouse/engine/ios *, from the Google warehouse pull by default. When you need to customize your changes, you can download the Engine source code and use the Ninja build system to generate them.

The final products of Flutter related code are app. framework(Dart code generation) and Flutter. Framework (engine). From the perspective of the Xcode project, Generated. Xcconfig describes the configuration information about the Flutter environment. Xcode_backend. sh, which is added to Build Phases in the Runner project Settings, makes a copy of the Flutter. Framework (from the engine of the Flutter repository to the Flutter directory in the Runner project root directory) and embed it. Compilation and embedding of app. framework. The content of Flutter generated in Runner. App is as follows:

Where Flutter_assets is the related resource and the code is app. framework and Flutter. Framework under Frameworks.

Run in Release mode

The rendering, event, and communication logic associated with Flutter is as follows:

Dart’s main function call stack is as follows:

Compiling in Debug mode

Compilation of flutter in Debug mode has a similar structure to Release mode, with two main differences:


Because it is Debug, there is JIT support in the Framework in this mode, but there is no JIT part in Release mode.


Unlike app. framework in AOT mode, which is the machine code corresponding to the Dart code, in JIT mode, App.framework has a few simple apis, and its Dart code is stored in snapshot_blob.bin. This part of the snapshot is a script snapshot, which contains the simple tokenized source code. All comments, whitespace characters are removed, and constants are normalized without machine code, tree shaking or obfuscation.

The symbol table in app. framework looks like this:

The Runner. The app/flutterassets/snapshotblob. Bin strings command can see the following content:

In Debug mode, the call stack for the main entry is as follows:

Compilation and Execution of Flutter code (Android)

In addition to some platform-related features, Android and iOS are similar in other logic, such as Release corresponding to AOT, Debug corresponding to JIT, etc., so only differences between the two are involved here.

Compilation in Release mode

In release mode, dart code in Android project under Flutter is as follows:

The vm/isolatesnapshotdata/instr content of arm instruction, which involved in the vm runtime services (gc), is used to initialize DartVM, call entrance to see DartInitialize dartapi. (h). The ISOLATE corresponds to our application DART code, which is used to create a new ISOLATE. The call entry is DartCreateIsolate(dart_api.h). Jar is similar to iOS, including the Engine part (libflutter. And the Embedder part (FlutterMain, FlutterView, FlutterNativeView, etc.). Practice flutter. The jar is located in the/bin/cache/artifacts of flutter warehouse/engine/android *, from the Google warehouse pull by default. To customize the flutter, download the Engine source and use the Ninja build system to generate flutter. Jar.

Isolatesnapshotdata/instr, for example, disarm command results are as follows:


Its Apk structure is shown as follows:

After APK is newly installed, a judgment logic (versionCode in PackageInfo combined with lastUpdateTime) is used to decide whether to copy assets in APK. The copied contents are as follows:

Isolate/vmsnapshotdata instr local data are finally in the app directory, and this part belongs to write content, can be downloaded and replace, complete the dynamic update of the app.

Run in Release mode

Compiling in Debug mode

Similar to iOS Debug/Release differences, Android Debug and Release differences mainly include the following two parts:


The difference with the iOS

2.App code

The snapshotblob.bin file in Flutterassets is the same as that in iOS.

After explaining how to build Flutter on iOS/Android, here is how to customize Flutter/Engine for customization and optimization. Since Flutter is an agile iteration, existing problems may not be problems later, so this part is not about how many problems can be solved, but how to solve different problems.

Customizations and optimizations related to Flutter construction

Flutter is a very complex system. In addition to the three-tier architecture mentioned above, Flutter Android Studio(Intellij) plugin, Pub repository management, etc. But our customizations and optimizations tend to be the tool-chain related logic of FLUTTER, which is located in the Flutter_tools package of the FLUTTER repository. Here is an example of how to customize this section.

The Android part

Related content includes flutter. Jar, libflutter. So, gensnapshot, flutter. Gradle, flutter(Fluttertools).

1. Restrict target to armeabi in Android

This section is build-related and the logic is located under flutter. Gradle. The default logic of flutter needs to be changed when apps support ARMv7 /arm64 via Armeabi. As follows:

Because of gradle’s inherent characteristics, this part of the change can be directly built to take effect.

2. Set Android to use the first launchable- Activity by default

This part is related to flutter_tools and modified as follows:

The point here is not how to change it, but how to make it work. In principle, flutter run/build/analyze/test/upgrade command execution actually is the flutter, flutter/bin/flutter) this script, Dart fluttertools.snapshot(generated by Packages/Fluttertools)

  1. if [[ ! -f "SNAPSHOT_PATH" ]] || [[ ! -s "STAMP_PATH" ]] || [[ "(cat "STAMP_PATH ")" ! = "revision" ]] || [[ "FLUTTER_TOOLS_DIR/pubspec.yaml" -nt "$FLUTTER_TOOLS_DIR/pubspec.lock" ]]; then

  2. rm -f "$FLUTTER_ROOT/version"

  3. touch "$FLUTTER_ROOT/bin/cache/.dartignore"

  4. "$FLUTTER_ROOT/bin/internal/"

  5. echo Building flutter tool ...

  6. if [[ "$TRAVIS" == "true" ]] || [[ "$BOT" == "true" ]] || [[ "$CONTINUOUS_INTEGRATION" == "true" ]] || [[ "$CHROME_HEADLESS" == "1" ]] || [[ "$APPVEYOR" == "true" ]] || [[ "$CI" == "true" ]]; then


  8. fi

  9. export PUB_ENVIRONMENT ="$PUB_ENVIRONMENT:flutter_install"

  10. if [[ -d "$FLUTTER_ROOT/.pub-cache" ]]; then

  11. export PUB_CACHE ="${PUB_CACHE:-" $FLUTTER_ROOT/. pub- cache"}"

  12. fi

  13. while : ; do


  15. "$PUB" upgrade --verbosity =error --no -packages -dir && break

  16. echo Error: Unable to 'pub upgrade' flutter tool. Retrying in five seconds...

  17. sleep 5

  18. done

  19. "$DART" --snapshot ="$SNAPSHOT_PATH" --packages= "$FLUTTER_TOOLS_DIR/.packages" "$SCRIPT_PATH"

  20. echo "$revision" > "$STAMP_PATH"

  21. fi

Copy the code

It is not hard to see to rebuild fluttertools, can remove flutterrepodir/bin/cache/fluttertools stamp (once such regenerated), or to block the if/fi judgment (every time to regenerate.

3. How to use Release mode Flutter in Android project Debug mode

If there is a lag in flutter during development, this may be due to logic or Debug mode. Build the APK under Release or force the flutter to change to release mode as follows:

The iOS section

Related content includes Flutter. Framework, Gensnapshot,, Flutter (flutter_tools).

1. Recompile caused by repeated replacement of Flutter. Framework during optimization build

This part of the logic is build-related and is in, To ensure that a Flutter gets the correct Flutter. Framework, every time a Flutter finds and replaces the Flutter based on the configuration (see Generated. Xcconfig config). Modified as follows:

2. How to use Release Mode Flutter in iOS engineering Debug mode

Set FLUTTERBUILDMODE to release and FLUTTERFRAMEWORKDIR to the path corresponding to release.

3. The armv7 support

Original articles refer to:

In fact, Flutter itself supports ARMV7 on iOS. However, there is no official support for FLUTTER under V0.3.1. You need to modify the relevant logic as follows:

A. Default logic to generate Flutter. Framework (arm64)

B. Modify Flutter so that Fluttertools can be rebuilt each time. Modify buildaot.dart and mac.dart, change arm64 for iOS to ARMv7, and change gen_snapshot to I386.

Gen_snapshot in the I386 architecture can be generated by running the following command:

  1. ./flutter/tools/gn --runtime-mode=release --ios --ios-cpu=arm

  2. ninja -C out/ios_release_arm

Copy the code

There is an implicit logic here:

Build CPU related predefined macros (x8664/ I386, etc.) for GenSnapshot, target Arch for GenSnapshot, and the architecture of the final app. framework should be consistent overall. X8664 -> X86_64 -> ARM64 or I386 -> I386 -> ARMV7.

C. On iPhone4S, EXCBADINSTRUCTION(EXCARMUNDEFINED) error will occur because genSnapshot generates unsupported SDIV instruction. This is done by adding the parameter — no-use-INTEger-division to genSnapshot (located in build_aot.dart). The logic behind this (dart compiling arm code logic flow) is shown below:

D. Generate a Flutter. Framework that supports both armV7 and arm64 based on the Flutter.

E. Modify info. plist under Flutter. Framework to remove it

  1.  <key>UIRequiredDeviceCapabilities</key>

  2.  <array>

  3.    <string>arm64</string>

  4.  </array>

Copy the code

Do the same for App. Framework to avoid being affected by App Thining.

Flutter_tools debugging

If you want to know how flutter builds apK in debug mode, you can use the following logic:

A. Learn the cli parameters of flutter_tools

B. Open Packages/Fluttertools as a DART project, modify fluttertools.dart based on the obtained parameters, and set the DART app on the command line to start debugging.

Customizing engine and debugging

Assume that we are customizing and developing flutter beta v0.3.1. In order to ensure stability, we do not update the SDK for a certain period of time. At this time, flutter master fixes a bug that has been on Flutter BETA V0.3.1, which is recorded as fixbugcommit. How can you track and manage this situation?

1. The flutter beta v0.3.1 specifies the corresponding engine commit as follows: 09 d05a389, see flutter/bin/internal/engine version.

2. Get the engine code

Custombetav0.3.1: customBetav0.3.1: CustomBetav0.3.1: CustomBetav0.3.1

4. Based on CustomBetav0.3.1 (commit: 09D05A389), execute gClient sync to get all engine code corresponding to Flutter Beta v0.3.1.

5. Use git cherry-pick fixbugcommit to synchronize master changes to CustomBetav0.3.1.

6. Execute the following code for iOS related changes:

  1. ./flutter/tools/gn --runtime-mode=debug --ios --ios-cpu=arm

  2. ninja -C out/ios_debug_arm

  3. ./flutter/tools/gn --runtime-mode=release --ios --ios-cpu=arm

  4. ninja -C out/ios_release_arm

  5. ./flutter/tools/gn --runtime-mode=profile --ios --ios-cpu=arm

  6. ninja -C out/ios_profile_arm

  7. ./flutter/tools/gn --runtime-mode=debug --ios --ios-cpu=arm64

  8. ninja -C out/ios_debug

  9. ./flutter/tools/gn --runtime-mode=release --ios --ios-cpu=arm64

  10. ninja -C out/ios_release

  11. ./flutter/tools/gn --runtime-mode=profile --ios --ios-cpu=arm64

  12. ninja -C out/ios_profile

Copy the code

Generate arm/ ARM64 & DEBUG /release/profile for iOS. Can be used to build product to replace flutter/bin/cache/artifacts/engine/ios * of flutter in the framework and gen_snapshot.

To debug the source code for Flutter. Framework, run the following command to build Flutter:

  1. ./flutter/tools/gn --runtime-mode=debug --unoptimized --ios --ios-cpu=arm64

  2. ninja -C out/ios_debug_unopt

Copy the code

Debug engine source code by replacing flutter. Framework and gen_snapshot in flutter with generated products.

7. Execute the following code for Android-related changes:

  1. ./flutter/tools/gn --runtime-mode=debug --android --android-cpu=arm

  2. ninja -C out/android_debug

  3. ./flutter/tools/gn --runtime-mode=release --android --android-cpu=arm

  4. ninja -C out/android_release

  5. ./flutter/tools/gn --runtime-mode=profile --android --android-cpu=arm

  6. ninja -C out/android_profile

Copy the code

Generate arm&DEBUG /release/profile artifacts for Android. Can be used to build product to replace flutter/bin/cache/artifacts/engine/android * gen_snapshot and flutter under the jar.

Subsequent topics

We will continue to share the following topics:

A. How Embedder handles rendering and event (click, etc.) delivery in the utter architecture, how threads and message loops are managed, and how channels work.

B. How Dart compilation and debugging works in Ngine and how rendering is handled inside Skia.

C. Ative project how to use Flutter to achieve progressive reconstruction and migration.

D. How to set up private warehouses and realize pub’s support for multiple warehouses


Contact us

If you have any questions or corrections about the content of the text, please let us know.

We are looking for talented candidates for flutter, C++, iOS/Android and Java.

Reference documentation

1.Flutter’s modes

2.iOS Builds Supporting ARMv7

3.Contributing to the Flutter engine

4.Flutter System Architecture

5.The magic of flutter

6.Symbolicating production crash stacks

8. Get the source code used in this article

Scan code to pay attention to [Xianyu Technology] public number