The author

๐Ÿ™‹ Author: Yangguang

The article framework

  • Flutter create what is flutter create?
  • What is flutter Build secretly doing behind his back?
  • Flutter Assemble is important
  • flutter run
    • Overall project structure analysis
    • Flutter Windows Desktop Run view
    • Flutter Windows Desktop startup process
    • Multithreaded model of flutter
    • The Dart VM is introduced

preface

Before starting this article, it is necessary to give a brief introduction to the whole framework of Flutter so that those who are not familiar with Flutter can read the rest of the article more smoothly.

Diagram of Flutter (image from Flutter website)


The diagram above shows the layered design of the frame of the Flutter, from bottom to top, corresponding to each other

  • The Embedder layer that Flutter contains on each platform is a platform-specific Embedder layer that connects to the operating system, enabling Flutter to use its operating system capabilities to access services such as surface rendering, accessibility and input, and to manage event loop queues. And by embedding layers that allow the upper layer to filter out operating system differences, flutter can be preconditioned to be cross-platform. The embedded layer is implemented in C++ on Windows and Linux, C++ and JAVA on Android, and Object-C and Object-C++ on macOS and ios. If The current embed layer of Flutter does not support your operating system platform, such as Cloud, you can even write a cloud embed layer to make Flutter support more platforms. Of course, just as an example, the Skia support does not support cloud nor is it clear.
  • The Engine layer, the most important layer of Flutter, is written in C++ and provides the underlying implementation of the core API of Flutter. Includes graphics (via Skia), text layout, file and network IO, accessibility support, plug-in architecture, and toolchains for the Dart runtime and build environments. The engine wraps the underlying C++ code as Dart code, which is exposed to the Flutter framework layer via Dart: UI.
  • The Flutter framework layer is a user-oriented layer that provides modern responsive frameworks written in the Dart language. It includes a rich set of platforms, layouts, and base libraries made up of a series of layers.

1. Run the Flutter Create Application command

Introduction to the create command

A project cannot be created without touching the flutter. The flutter create command supports the generation of project templates for a specific platform or type, as shown in the following command

Create a appliation flutter create myApplication. Create application flutter create D:\\MyDir\ myApplication Create a plugin for flutter create --template=plugin myplugin // View help for flutter create-hCopy the code

Tempalte options

The value of the template option determines the project template that will be created by the flutter create command

[app] (default) Generate a Flutter application. [module] Generate a project to add a Flutter module to an existing Android or iOS application. [package] Generate a shareable Flutter project containing modular Dart code. [plugin] Generate a shareable Flutter project containing an API in Dart code with a platform-specific implementation for Android,  for iOS code, or for both. [skeleton] Generate a List View / Detail View Flutter application that follows community best practices.Copy the code

Platform options

Flutter supports create ios, Android, Windows, Linux, MAC, Windows, and Web projects. If the platform option is not specified, all systems will be created.

flutter create --platform=windows D:\Code\flutter-code\myapplication
Copy the code

Flutter Windows project initial structure

The Windows directory

Focus on the Windows directory, as shown above, which is a C++ project that uses CMake to build system management. Seeing that it is a CMake project, we tried to generate a visual studio solution directly using CMake without the help of flutter build.

Generate_config. cmake was missing. After looking carefully at the Windows template generated by Flutter, we found that many files were missing, such as internal reference flutter_windows.h, linked dynamic library, etc. For now the source of the flutter build command may answer my question.

2. Build the mode environment of Flutter upper framework source code

The command line tools for Flutter are provided by the superstructure of Flutter, written in Dart. In order to analyze the subsequent commands that Flutter provides, it is necessary to build a modal environment.

By default, you have already installed Flutter yourself before setting up a Flutter environment. For convenience, use the source mode directly on the installed Flutter.

Found the Flutter installation directory

where flutter
Copy the code

Find the source path and use VsCode to open it

VS Code modality environment setup

Open the VSCode launch.json file and search globally for Packages \\flutter_tools (the source of the flutter command is in this project).

The BUILD_ROOT configuration is supported

Dart: run (1151) Context. run The first line of the function

return context.run<void>(
      name: 'command',
      overrides: <Type, Generator>{FlutterCommand: () => this},
      body: () async {
        // The following four lines are the code to be added
        final Map<String.String> env = Platform.environment;
        if (env.containsKey("BUILD_ROOT")) {
          globals.fs.currentDirectory = env["BUILD_ROOT"]; }/ /...},)Copy the code

You can also modify the CWD configuration of vscode’s current working directory to do this.

Launch. The json configuration

{
	"name": "flutter_tools"."cwd": "packages\\flutter_tools"."request": "launch"."type": "dart"."program": "lib\\executable.dart"."args": [
		"build",]."flutterMode": "debug"."env": {
		"FLUTTER_ROOT": Your Flutter root directory."BUILD_ROOT": "Directory of projects you need to build"}}Copy the code

You only need to modify the args parameter to perform other command modulations.

3. Flutter Build command

When did the missing files in the cmake project complete? The flutter build command does the following things via the flutter build source mode.

  1. inwindows\flutter\ephemeralCreate one in the directorygenerated_config.cmakeWhich records some of the environment variables of flutter and the build project
  2. Create a new one.plugin_symlinksfile
  3. Update the CMake file
  4. Run the cmake config command to configure the C++ project
  5. Execute the cmake build command to build the C++ project
  6. Run the cmake install command

The above steps did not solve all my questions. I only found the generation of generated_config.cmake, such as when the flutter engine dynamic library was copied to the runtime directory, and when the incomplete C++ project dependencies were copied. The trick now seems to be to find cmake files.

CMake User-defined command

file: windows/flutter/CMakeLists.txt

add_custom_command(
  OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
    ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN}
    ${CPP_WRAPPER_SOURCES_APP}
    ${PHONY_OUTPUT}
  COMMAND ${CMAKE_COMMAND} -E env
    ${FLUTTER_TOOL_ENVIRONMENT}
    "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
      windows-x64 $<CONFIG>
  VERBATIM
)
Copy the code

Explanation: ${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat. OUTPUT is the OUTPUT of the CMake build. COMMAND ${CMAKE_COMMAND} -e env ${FLUTTER_TOOL_ENVIROMENT} ${FLUTTER_TOOL_ENVIROMENT} is defined as follows. These environment variables are passed to tool_backend.bat.

file: windows\flutter\ephermeral\generated_cofig.cmake

# Generated code do not commit.
file(TO_CMAKE_PATH "C: \ \ Users \ \ Administrator \ \ Documents \ \ flutter_windows_2 5.3 stable \ \ flutter." " FLUTTER_ROOT)
file(TO_CMAKE_PATH "D:\\Code\\flutter-code\\myapplication" PROJECT_DIR)

# Environment variables to pass to tool_backend.sh
list(APPEND FLUTTER_TOOL_ENVIRONMENT
  "FLUTTER_ROOT = C: \ \ Users \ \ Administrator \ \ Documents \ \ flutter_windows_2 5.3 stable \ \ flutter." "
  "PROJECT_DIR=D:\\Code\\flutter-code\\myapplication"
  "FLUTTER_ROOT = C: \ \ Users \ \ Administrator \ \ Documents \ \ flutter_windows_2 5.3 stable \ \ flutter." "
  "FLUTTER_EPHEMERAL_DIR=D:\\Code\\flutter-code\\myapplication\\windows\\flutter\\ephemeral"
  "PROJECT_DIR=D:\\Code\\flutter-code\\myapplication"
  "FLUTTER_TARGET=D:\\Code\\flutter-code\\myapplication\\lib\\main.dart"
  "DART_DEFINES=RkxVVFRFUl9XRUJfQVVUT19ERVRFQ1Q9dHJ1ZQ=="
  "DART_OBFUSCATION=false"
  "TRACK_WIDGET_CREATION=true"
  "TREE_SHAKE_ICONS=false"
  "PACKAGE_CONFIG=D:\\Code\\flutter-code\\myapplication\\.dart_tool\\package_config.json"
)
Copy the code

tool_backend.bat

file: FLUTTER_ROOT\packages\fluttertools\bin\tool_backend.bat

Bat executes the tool_backend.dart code. The tool_backend.dart code then runs the flutter Assemble command.

cmake install

install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
  COMPONENT Runtime)

install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
  COMPONENT Runtime)

install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
  DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)

message("AOT_LIBRARY : ${AOT_LIBRARY}")

# Install the AOT library on non-Debug builds only.
install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"CONFIGURATIONS Profile; Release COMPONENT Runtime)Copy the code

The cmake install command is executed after the cmake build is complete and copies directories such as AOT build artifacts, resource files, and executable files.

4. Flutter Assemble orders

We know that cmake executes the flutter Assemble command during build. Now look at what the flutter Assembble does.

Flutter assemble launch.json configuration is as follows:

{
	"name": "flutter_tools"."cwd": "packages\\flutter_tools"."request": "launch"."type": "dart"."program": "lib\\executable.dart"."args": [
		"assemble"."--no-version-check"."--output=build"."-dTargetPlatform=windows-x64"."-dTrackWidgetCreation=true"."-dBuildMode=release"."-dTargetFile=lib\\main.dart"."-dTreeShakeIcons=true"."-dDartObfuscation=false"."release_bundle_windows_assets"]."flutterMode": "debug"."env": {
		"FLUTTER_ROOT": Your Flutter root directory."BUILD_ROOT": "Your project root directory"}}Copy the code

The whole operation process of flutter Assemlbe

UnpackWindows

file: windows.dart : UnpackWindows

UnpackWindows will copy the files (.h,.cc,.dll) required by the previous CMkae build to the specified directory. The specific files can be viewed in the unpack_windows.stamp file in your application project root directory. For example, Will FLUTTER_ROOT/bin/cache/artifacts/engine/Windows – x64 – release/flutter_windows. DLL copied to project_dir/Windows/flutter/ephemera L.

The Dart to compile

file: common.dart: KernelSnapshot file: compile.dart: KernelCompiler file: common.dart: AOTElfDart

KernalSnapshot and AOTElfBase are responsible for compiling the DART code. Dart compilation takes a different form depending on the build mode, and AOTElfBase is skipped in DebBug mode.

Dart Release and Debug compilation

  • Dart is compiled In Debbug mode In JIT (Just In Time) mode to produce an intermediate language (app.dill) that enables efficient hot overloading
  • Dart is compiled in Ahead Of Time (AOT) mode under the Release model to generate machine code (app.so) for the corresponding platform, which improves execution efficiency. However, JIT compilation is also required for AOT mode compilation

Windows build product

The Release of Flutter Windows is divided into two parts. The first part is generated by dart code, which uses the Frontend_server compiler to convert dart code into an AST abstract syntax tree, and generates the Dart kernel product of app.dill. After a series of optimizations for the intermediate code (app.dill) using gen_snapshot in release mode, different binaries are generated according to different platforms. Linux ELF binaries are used for Windows, Linux and Android binaries. The second part is generated by the flutter engine code, which is usually already compiled, and finally compiled by the C++ project template created by flutter create to generate an executable binary.

The compiled product of Dart can be viewed in the./dart_tool/flutter_build directory of the current project root

WindowsAotBundle & BundleWindowsAssests

file: windows.dart: WindowsAotBundle file: windows.dart: BundleWindowsAssets

WindowsAotBundle and BundleWindowsAssets simply copy previously generated artifacts, such as Dart compiled app.dill (which will be renamed kenerl_blob.bin), app.so, assets, etc.

Assets can be viewed in build/flutter_assets in the current project root directory (kenerl_blob.bin is also in this directory) app.so can be viewed in Build/Windows in the current project root directory

A small summary

  1. Flutter create creates a flutter project template that contains an incomplete C++ project
  2. The flutter build command first generates the cmake_generated_. Cmake file so that its cmake config can be executed normally, and then builds the C++ project by executing the cmake build command. So the tool_backend.bat script is invoked using the cmkae command before cmake compiles the project
  3. The tool_backend.bat script executes the tool_backend.dart code. The tool_backend.dart code executes the flutter assemble command
  4. The flutter assemble command first generates the C++ files and libraries required for the cmake build, then compiles the Dart code and copies the build product to the appropriate location
  5. After executing tool_backend.bat, C++ code is compiled to generate application.exe
  6. The flutter build uses the cmake install command to copy the resources that the application. Exe runtime depends on to the runtime directory

5. Flutter Run

After the execution of the flutter build can be in the root directory of the current project build/Windows/runner/Release directory to see the final flutter generated by the product of the project, this directory can be packaged directly, but the flutter of c + + compilation mode is the MD of the project, So the final package also needs to copy the runtime environment required by Windows C++ into the Release directory.

Flutter release directory

DLL โ”‚ โ”‚ flutter_windows. Myapplication. Exe โ”‚ โ”” โ”€dataโ”‚ โ”œโ”€ โ”œโ”€ MaterialIcons โ”‚ โ”œโ”€ โ”œโ”€ MaterialIcons โ”‚ โ”œโ”€ MaterialIcons โ”‚ โ”œโ”€ MaterialIcons โ”‚ โ”œโ”€ MaterialIcons โ”‚ โ”œโ”€ MaterialIcons โ”‚ โ”œโ”€ MaterialIcons โ”‚ โ”œโ”€ MaterialIcons โ”‚ โ”œโ”€ MaterialIcons โ”‚ โ”œโ”€ MaterialIcons-Regularโ”‚ โ”œ โ”€ garbage โ”œ โ”€ honey sci-imp 1.txt โ”‚ โ”œ โ”€ garbage sci-imp 1.txtCopy the code
  • App. so is a Dart AOT product
  • Icudtl. dat is an internationalization file
  • There are some fonts, ICONS and so on in Flutter_assets

Build a Flutter Windows Application (C++ project) environment

Because the operation view and startup process of Flutter Windows Application need to be analyzed later, the source mode cannot be avoided. Here is a simple way to build the environment. Of course, if you want a detailed and convenient analysis of the Source of the Flutter Engine, you can also recompile the Flutter engine yourself.

1. Download the flutter Engine source code

git clone https://github.com/flutter/engine.git
Copy the code

Download the Flutter source code directly. Of course, if you also want to perform adjustments in the SKia or DART VM code, You need to download Google’s depot_tools and perform Glient Sync to update the third-party library code.

2. Switch to the engine branch corresponding to the specified local FLUTTER version

# Check the engine used by local flutter
flutter --version

# Change to Revision
git checkout your_revision
Copy the code

3. Add the PDB symbol file

In vs tools – > options – > mode – > symbol add your_project_path/Windows/flutter/ephemeral path.

4. Run the program and make sure the symbol is loaded (myApplication.exe)

You can then open the Flutter Engine source code in Visual Studio to tune.

Flutter Windows Application run view

Flutter start process

View with Flutter Windows Application running view

  • Flutter Applicationn consists of a FlutterWindow which contains a Win32 main window. In the main function, a FlutterWindow is created and a Win32 main window is created. FlutterWindow listens for win32 window creation events and initializes the FlutterViewController object in the Windows window creation callback
  • The FlutterViewController mainly displays engine rendering results to the corresponding FlutterView. It creates FlutterView and FlutterEngine. FlutterView and FlutterEngine are embedded through the Embedder API. That is, the C API provided by Flutter_windows. h calls the Flutter embedding layer and tells the embedding layer to create FlutterWindowsView and FlutterWindowsEngine. FlutterWindowsView, the real object of FlutterView, is actually a Windows window, which was set as a child window of the main window in the main window creation event of Win32
  • When FlutterViewController is created, it also creates threadHosts and shells through the embedded layer engine. The Shell is very important, it is the central nerve of flutter. Responsible for engine creation, Root Isolate creation, DartVM startup, PlatformView creation, etc. After Shell creation, The start Shell will trigger the start of the Flutter engine. When the engine starts, DartVM will be created and then the Dart _runMainZoned function will be called. Dart Main will be called after _runMainZeoned

Multithreaded model of Flutter

Create a ThreadHost in the embedded layer engine. The ThreadHost creates four threads: Platform Thread (this Thread is not created by the flutter but directly taken over by the main Thread), UI Thread, Rasterizer Thread, AND IO Thread. And these four threads maintain four taskrunners. TaskRunner, TaskRunner, TaskRunner, the principle is similar to thread communication in Chromium, which has an internal MessageLoop to loop and wait and process tasks in the message queue. Each TaskRunner has its own responsibilities, which are as follows:

  • Platfom TaskRunner: Runs on the main thread, which handles listening for user input times, keyboard and mouse events, and handles external and engine layer communications
  • UI TaskRunner: Runs on the UI thread and is responsible for rendering the engine
  • Rasterizer TaskRunner: Mainly responsible for GPU-related work
  • IO TaskRunner: I/O is mainly responsible for performing time-consuming operations, such as reading local images, decompressing them into a format that can be processed by the CPU, and Posting them to the GPU TaskRunner

The Dart vm

The Dart virtual machine and Root Isolate are created with the start of the whole Flutter engine.

Dart is a single-threaded language. All code is executed sequentially. Dart provides a mechanism called Isolate, which enables Dart to support concurrent programming. But the form and process are particularly similar.

DartVM also has its own Isolate, which is completely managed by the VMS themselves and cannot be directly accessed by the Flutter engine. Dart ui-related operations are performed by Root Isolate using Dart C++ or by sending message notifications to UI TaskRunner, which can interact with Flutter engine modules.

  • The heap inside the Isolate’s internal implementation is the MEMORY store managed by the GC that runs all objects allocated by the code in the Isolate
  • The Isolate heap can reference objects in the VM Isolate heap, but the VM Isolate cannot reference the Isolate heap
  • The Isolate is completely isolated from the Isolate and cannot reference each other
  • Each Isolate has a Mutator Thread that executes the DART code and a Helper Thread that handles tasks within the VIRTUAL machine (such as GC and JIT). The Isolate can have multiple virtual machines, but like processes, they do not share memory with each other. No direct access, only communication through Dart’s unique Port.

Refer to the link

Flutter architectural overview | Flutter

The Engine architecture ยท flutter/flutter Wiki ยท GitHub

Wiki ยท GitHub The flutter tool ยท flutter/flutter Wiki ยท GitHub

Evolution and Flutter cross-platform architecture begins – Gityuan blog | Yuan Huihui technology blog