Flutter Analysis and Practice: Quick Building of Hybrid Projects

Image for post
Image for post

Flutter provides two main development modes. One is a standalone app mode, in which Flutter is the principal component and native projects are included in Flutter projects. The other one is the Flutter module mode, in which Flutter serves as a module and can be integrated as a dependency to the existing iOS and Android native applications. In this case, native projects can be under any directory and are not associated with Flutter project addresses. Therefore, you must declare the local Flutter project address in the native project structure. As the team that built the Alibaba Xianyu app, we attempted to use hybrid app architectures for a long time, 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.

1.4.1 flutter-boot Overview

Essentially, flutter-boot solves the two problems of 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 problem, 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 is 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-boot is available immediately upon creation. Now, let’s look a bit more closely at how flutter-boot solves these problems.

Image for post
Image for post
Figure 1–19

1.4.2 Project Design

Before studying the flutter-boot project design in detail, you must first understand Flutter’s add-to-app feature provided by Google. The add-to-app feature shows you how to create Flutter as a module. The project structure of the Flutter module is:

some/path/
my_flutter/
lib/main.dart
.ios/
.android/

Under the official project structure, .ios and .android are template projects. When running in the Flutter project directory, both projects are used to start apps. Now, we need to learn how to associate native projects with Flutter. The association involves three parts: the Flutter framework, business code, and 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.) Table 1–1 describes the differences between the four parts, including the Flutter framework, Flutter plug-in native, Flutter plug-in Dart, and Flutter business code:

Image for post
Image for post

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. Like business code, it must support the debug and release modes of 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 the 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.

When using the official hybrid project, we may encounter the following problems:

  • Files or configurations are manually added, which takes a long time.
  • Native projects cannot be run in Flutter repositories.
  • Remote 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.

1) Create

The create process is to build a Flutter module and deploy a Git repository. Before running the Flutter module creation command, perform a basic check to ensure that the project location and naming conventions meet official requirements. During Git repository deployment, the files in gitignore will be ignored. Then, 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.

2) Link

The link process is to associate local native projects with Flutter projects. During association, first initiate a request to obtain the addresses of the Flutter project and native project. 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.

3) 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. Consequently, 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, only the engineer who initiates the hybrid project needs to run remotelink once, while other development collaborators do not have to worry about the configuration process for remote building.

4) init

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.

1.4.3 Hybrid Stack

Hybrid stack is an open-source framework used by Xianyu 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:

To achieve quick integration, the two problems must be solved: version compatibility between Flutter and the hybrid stack, and encapsulation and insertion of hybrid stack demo code.

1) Version Compatibility

The current hybrid stack version is 0.1.52, which supports Flutter 1.5.4. When Flutter is upgraded, the version of the integrated hybrid stack must be adapted accordingly. 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.

2) 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, and native testing portal.

1) 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.

2) Page Routing Configuration and Demo Dart Page

When routing to an identifier, the Flutter page or the native page must identify the identifier and redirect it to the corresponding page. Routing configuration must be deployed on both the native page and the Flutter page. On the native side, simplify the demo routing code of the hybrid stack and 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.

3) 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 several 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.

Original Source:

Written by

Follow me to keep abreast with the latest technology news, industry insights, and developer trends.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store