By Xing Wang, Xiang Zhiming, and Ma Yin
Since its emergence, Flutter has been a leader in cross-terminal development, with more and more domestic and overseas companies exploring its capabilities. Flutter provides two main development modes. The first is a stand-alone app mode, in which Flutter is the principal component and native projects are included in Flutter projects. In the second mode, Flutter serves as a Flutter module integrated in an existing native iOS or Android application. In this case, native projects can be under any directory and are not associated with Flutter project addresses, and therefore you must declare the local Flutter project address in the native project architecture.
As the team that built the Alibaba Idle Fish app (or Xianyu in Mandarin), we had long attempted to use hybrid app architectures and also made many changes to our native projects before Flutter could serve as a module. After the official release of the Flutter module mode, we did a great deal of research and ultimately released flutter-boot, an out-of-the-box scaffold for hybrid projects, to help users quickly build hybrid projects.
Essentially, flutter-boot solves two problems in the hybrid development mode: the project design and hybrid stack for Flutter hybrid development. Let’s take a look at how flutter-boot solves these problems.
First, to solve the project design issue, flutter-boot established a standardized project creation process and user-friendly interactive commands. After the process is completed, you will have a standard project structure for hybrid development. This project structure will help give you both Flutter and Native development perspectives. Flutter can be integrated in two ways, which are local Flutter development and cloud-based Flutter building. The basic structure of both modes are shown in Figure 1–1.
In addition, to solve the hybrid stack problem, flutter-boot can automatically inject hybrid stack dependencies and encapsulate and inject important hybrid stack access code into a native project. After you insert several lines of simple template code as prompted, the hybrid stack will take effect.
The hybrid project that is built by using flutter-bot is available immediately upon creation. Now, let’s look a bit more closely at how flutter-boot solves these problems.
The Official “Add Flutter to Existing Apps” Feature
Before studying flutter-boot project design in detail, you must first understand the “Add Flutter to existing apps” feature provided by Google.
The “Add Flutter to existing apps” feature shows you how to create Flutter as a module. The project structure of the Flutter module is as follows:
Under the official project structure, .ios and .android are template projects for running Flutter. When running in the Flutter project directory, both projects are used to start apps. Now, we need to learn how to associate native projects. Here, the association is divided into three parts: the Flutter framework, Flutter business code, and Flutter plug-in library. The Flutter plug-in library is divided into two parts: the Flutter plug-in native (plug-in native code) and the Flutter plug-in dart (plug-in dart code). The following is the differences among the four parts including the Flutter Framework, Flutter plug-in native, Flutter plug-in dart, and Flutter business code:
Therefore, the Flutter Framework only needs to be declared in dependency management. the Flutter plug-in native can be directly integrated into the source code, whereas the Flutter plug-in dart is valid only when it is referenced by the business code. Therefore, like the business code, it must support the debugging and publishing modes for dart code. As a result, the dart code association is incorporated into the app building process, and the dart code building mode is determined by the app building mode. Let’s take a look at the specific implementation by using iOS as an example. First, add a custom call for the podfilehelper ruby script in the podfile file. In this case, podfilehelper declares Flutter Framework dependencies, the reference to the Flutter plug-in native, and the business code path. Next, intervene in the building process by adding a call for the xcode_backend shell script in the building phase of xcode. In this case, xcode_backend produces the dart building product based on the current building mode.
Benefits from Flutter-boot
When using the official hybrid project feature, we encounter the following problems:
- Manually adding files or configurations takes a long time.
- Native projects cannot be run in Flutter repositories.
- Remote machine building is not supported when Flutter is deployed as an independent code repository.
To solve these problems, we divide the deployment of hybrid projects into four processes that include create, link, remotelink, and update in the flutter-boot scaffold.
The create process is used to build a Flutter module and includes Flutter module creation and git repository deployment. Before calling the Flutter module creation command, perform a basic check to verify that the project location and naming conventions meet official requirements. During Git repository deployment, the files in gitignore will be ignored. Meanwhile, check the status of the repository. If the repository is empty, directly add the files. If it is not empty, clear up the repository first.
The link process is used to associate native projects and Flutter projects. During association, first initiate a request to obtain the addresses of the Flutter projects and native projects. Then, use a script to automatically integrate the components that had to be manually integrated in the official process. To use the Flutter development perspective, where native projects are run under Flutter projects, soft-link the native projects to link them to the ios and android directories of the Flutter project. To run Flutter, first go to the ios or android directory under the project and run the native projects from there. iOS projects run in a Flutter project are subject to a restriction: the target of the iOS project must be specified as “runner”. To solve this problem, copy the primary target of the native project to create a replica target named “runner”.
At the same time, to support the remote building mode, the declaration of the local Flutter repository path must be differentiated according to the exact building mode and encapsulated in a custom dependency script. For example, in an iOS project, you must add the fbpodhelper.rb script file. Then, add the local path of the Flutter repository to the fbConfig.local.json configuration file.
Remotelink and Update
The remotelink process is to obtain the code of the Flutter repository in the remote building mode and build it on a remote machine. In the remote building mode, you must intervene in the dependency management process. When obtaining dependencies, pull the Flutter repository code, place it in the .fbflutter directory of the native project, and declare this directory as the local Flutter repository path. The process of pulling the Flutter code and deploying it locally is called the update process. As a result, this makes the remote building process the same as the local building process.
To distinguish between the remote mode and the local mode, record the remote Flutter repository information in fbConfig.json and ignore the fbConfig.local.json file in gitignore. In this way, the engineer who initiates the hybrid project only needs to run remotelink once, while other development collaborators do not have to worry about the configuration process for remote building.
To facilitate the building process, we provide a command set named init and have integrated the necessary steps into the init commands in the command-line interaction mode.
Hybrid stack is an open-source framework used by Idle Fish to coordinate the interactions between native pages and Flutter pages in a Flutter hybrid project. It is currently the major framework in the hybrid development mode. After the hybrid stack is made open-source, if a large number of developers integrate the hybrid stack, various environment configurations are produced and code is added, which can lead to problems with integration. Therefore, we decided to provide a fast integration solution. To achieve fast integration, we had to solve two problems:
- Version compatibility between Flutter and the hybrid stack
- Encapsulation and insertion of hybrid stack demo code
Currently, the supported hybrid stack version is 0.1.52, which supports Flutter 1.5.4. When Flutter is upgraded, the hybrid stack must be adapted accordingly, which means you must change the integrated hybrid stack version. Therefore, you should maintain the version configuration of the hybrid stack with a file and record the current hybrid stack version required by Flutter. In the initial version of flutter-boot, we restricted the version number of the hybrid stack. In future versions, we will provide the version selection function.
Code Encapsulation and Insertion
After looking into the hybrid stack utilization process, we divided the demo code required by the hybrid stack into four parts:
- Flutter engine hosting
- Page routing configuration
- Demo dart page
- Native test redirection entry
Flutter Engine Hosting
For engine hosting, we rely on app initialization. A line of code is added as the interface because the complexity of the initialization process increases along with the complexity of the app. During initialization, you can add this line of code to implement hosting.
Page Routing Configuration and Demo Dart Page
When routing to an identifier is configured, the Flutter or native page must identify and redirect to the corresponding page. The route configuration must be deployed on both the native and Flutter sides. On the native side, simplify the demo routing code of the hybrid stack and then add the simplified code to a fixed directory in the native project. We have encapsulated an iOS code addition tool for inserting files because code files added only in iOS are excluded from the building scope. On the Flutter side, overwrite the main.dart file, integrate the main.dart file that provides routing logic, and provide the creation logic of the demo dart page.
Native Test Redirection Entry
To help you quickly view the redirection mode of a hybrid project, we have encapsulated an entry button and a button addition process for both iOS and Android. With them, you can manually add a line of code on the test page to view the Flutter redirection entry.
Previously, developers might spend several days building a hybrid project. With flutter-boot, you only need to call one command and add two lines of code to build a hybrid project, greatly reducing your development costs. However, flutter-boot has not yet reached its full potential, and we hope to further simplify Flutter development for users. In the future, we will optimize the collaborative development process and improve the building process of the continuous integration environment to provide an even better development experience.