This article is part of Alibaba’s Utilizing Flutter series.

For developers, software development kits like Google’s Flutter offer a means of building native Android and iOS mobile interfaces while avoiding many of the associated burdens, including sheer time demand. Still, some may wonder how Flutter projects and purely Android or iOS projects differ, for instance in their rendering or event delivery mechanisms, or in how they enable developers to modify errors and implement a project when construction stalls.

To help address these concerns and shed light on its workings, Alibaba looks today at Flutter from a global perspective, using a “hello_flutter” example project. The following sections will first introduce Flutter’s principles before then discussing customization and optimization capabilities, with specific steps that developers interested in exploring Flutter can follow.

Flutter’s Fundamentals

Architecture

Flutter’s architecture as a program is founded on three distinct layers: its framework, its engine, and its embedder.

Overview of Flutter’s architecture

Flutter’s framework is implemented in Dart, and encompasses material design–style widgets, Cupertino-style widgets (for iOS), text/image/button-based widgets, rendering, animation, gestures, and so on. The core code for this layer encompasses flutter packages under the flutter repository and packages under the sky_engine repository (the dart:ui library provides the interface between the flutter framework and engine) such as io, async, and ui packages.

Flutter’s engine is implemented in C++, and includes Skia, Dart, and Text. Skia is an open-source 2D 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 include Windows7+, macOS 10.10.5+, iOS8+, Android4.1+, Ubuntu14.04+, and so on.

The engine’s Dart section mainly encompasses Dart Runtime and Garbage Collection (GC). If Flutter is running is in debug mode, JIT (Just in Time) support is also included, while in release and profile modes, dart code is compiled into the native “arm” code by AOT (Ahead of Time), such that no JIT exists. Text refers to text rendering with the following rendering levels: the libtxt library (for font selection and separating lines), derived from minikin and HartBuzz (used for glyph selection and shaping). Skia acts as a render backend, using FreeType rendering on Android and Fuchsia and CoreGraphics rendering on iOS.



The embedder is an embedded layer that embeds Flutter in various platforms. The main tasks here are rendering Surface settings, thread settings, and plug-ins. We can see that the platform-related layer of Flutter is minimal, where the platform (such as iOS) simply provides a canvas and the rest of the rendering-related logic occurs inside Flutter, making for good cross-platform consistency.

Engineering structure

The development environment used in this article is Flutter beta v0.3.1, corresponding to the engine commit : 09d05a389.

For our simple “hello_flutter” project example, Flutter’s engineering structure is as follows:

In the above example, “ios” is the code for iOS, using CocoaPods to manage dependencies, “android” is the code for Android, using Gradle to manage dependencies, and “lib” is the dart code, using pub to manage dependencies. Similar to the Podfile and Podfile.lock for Cocoapods in iOS, the corresponding ones in pub are pubspec.yaml and pubspec.lock.

Modes

Flutter supports common modes including debug, release, and profile, but does so with some notable distinctions.

Flutter’s debug mode corresponds to Dart’s JIT mode, also known as the check mode or slow mode, and supports devices and emulators for iOS and Android. Under this mode, the assertion functions, including all debugging information, service extensions, and debugging aids such as “observatory,” are enabled. This mode is optimized for rapid development and operation, but not for execution speed, package size, or deployment. Under this mode, compilation is based on JIT technology, supporting the popular sub-second stateful hot reload.

Flutter’s release mode corresponds to Dart’s AOT mode, which is targeted for deployment to end users and supports real machines but not emulators. Under this mode, all assertions are disabled, and in order to remove as much debugging information as possible, all debugging tools are also disabled. This mode is optimized for quick launch, speedy execution, and packet size, while all debugging aids and service extensions are prohibited in it.

Flutter’s profile mode is similar to its release mode in that this mode only adds supports for service extension and tracking, and minimizes the dependencies needed to use tracking information. For example, “observatory” can be connected to processes. The reason that the profile mode does not support the emulator is that diagnoses on the emulator do not represent real performance.

Since Flutter’s profile mode and release mode have no differences in terms of compilation principles, this article only discusses its debug mode and release mode.

In fact, an iOS or an Android project carried out in Flutter is still a standard iOS or Android project. Flutter only generates and embeds an App.framework and Flutter.framework (iOS) by adding a shell in BuildPhase and compiles related code and embeds it in the native app by adding flutter.jar and vm/isolate_snapshot_data/instr (Android) through Gradle. Therefore, this article mainly discusses principles of construction and execution introduced by Flutter. Although compiling targets include arm, x64, x86, and arm64, the principles are similar enough to discuss only the principles related to arm. (If there is no special description, android defaults to armv7.)

Going Deeper: Code Compilation and Execution for iOS

Compilation in release mode

Under the release mode, the dart code’s build process for the iOS project under Flutter is as follows: