Flutter Analysis and Practice: Hybrid Project Transformation

Image for post
Image for post

When Flutter is used for cross-platform development but the original iOS and Android projects are very large, developers need to consider how to seamlessly integrate Flutter to these projects without affecting the development efficiency.

This article provides a general project transformation scheme as a reference for teams who want to transfer to Flutter.

1.2.1 Project Background and Problems

The structure of a Flutter project is complex. The project directory of Flutter consists of the native project directories (iOS and Android directories), as shown in Figure 1–12. By default, the native project where Flutter is introduced cannot be built or run independently of its parent directory, because it depends on Flutter-related libraries and resources.

Image for post
Image for post
Figure 1–12

Obviously, with the native project, developers are unlikely to create a new Flutter project or rewrite the entire app. To use the existing native project in Flutter, we include the native projects in the Flutter project. This creates a series of problems in the following aspects:

  1. Building and Packaging: After Flutter is introduced to a native project, the project cannot be independently compiled or built due to its dependency on and coupling with Flutter. In the Flutter environment, a project starts to be built when you run the Flutter building command. The execution process includes the building of the native project. Developers need to configure the complete Flutter runtime environment to complete the entire process.

1.2.2 Transformation Objectives

To resolve the preceding problems, we propose the following transformation objectives to minimize the dependency of native projects on Flutter-related files:

  • Native projects can be independently compiled, built, and debugged to minimize the interference to developers. This decouples the packaging platform from the Flutter environment and processes.

1.2.3 Solution Formulation

1.2.3.1 Two Development Modes

Flutter offers two development modes: standalone mode and Flutter mode. In standalone mode, a native project is in an independent directory. In Flutter mode, a native project is in the Flutter directory. Native development and packaging are completed in standalone mode. In this case, Flutter is transparent to developers and the packaging platform and does not affect the building and debugging. Flutter code is developed in Flutter mode. The generation, compilation, and debugging of its related libraries follow the Flutter flow, as shown in Figure 1–13.

Image for post
Image for post
Figure 1–13 ¨C Two Development Modes

1.2.3.2 Dependency Clarification

From the definition of the preceding two modes, the core of the transformation is to extract the standalone mode, so it is necessary to clarify the dependency of the standalone mode on Flutter and extract it into a third-party library, resource, or source code file. For example, for an iOS project, the Flutter build source code shows that the Xcode project depends on the following Flutter items:

1) App.framework: the Dart business source code file.

2) Flutter.framework: the Flutter engine library file.

3) pubs plug-in directory and index files: the Flutter plug-ins for various systems and their custom channels (bridges.)

4) flutter_assets: the static resources that Flutter depends on, such as fonts and images.

1.2.3.3 Dependency Policies

During the transformation, Xianyu tried out the following two dependency policies:

1) Local Dependencies ¨C By modifying the Flutter building process, Xianyu places the library files, source code, and resources in the subdirectory of the native project for reference. For example, for an iOS native project, Flutter.framework and related plug-ins are packaged into local pod dependencies, which are also replicated to a local directory for maintenance. Therefore, the standalone mode supports independent building and running. For native developers, Flutter is only a collection of second-party libraries and resources, which does not need to be concerned. In Flutter mode, the building flow of Dart source code remains unchanged and does not affect compilation and debugging. Changes of a local dependency in Flutter mode can also be synchronized to a subdirectory of the native project in real time. After the changes are committed, the standalone mode has the latest Flutter-related features.

Advantages: It is convenient to synchronize Flutter-related content changes to the standalone mode.

Disadvantages: Some complex changes need to be made to the original Flutter building flow, which will conflict with Flutter code merging. In addition, the native project and Flutter code, libraries, and resources are still coupled locally, which is not independent enough.

2) Remote Dependencies ¨C To implement remote dependencies, all Flutter dependencies are stored in an independent remote repository. Relevant resources, source code, and library files in the remote repository are referenced in standalone mode, and the building flow and reference method in Flutter mode remains unchanged, as shown in Figure 1–14.

Advantages: The Flutter building flow is slightly changed, completely solving the problem of local coupling.

Disadvantages: The synchronization process becomes more complicated. Flutter-related content changes take effect only after being synchronized to the remote repository and then to the standalone mode.

Image for post
Image for post
Figure 1–14

1.2.4 Implementation of Transformation

1.2.4.1 Directory Structure

In Flutter mode, the iOS and Android subdirectories in the parent project directory contain corresponding native projects. For code management, subprojects can use a submodule of Git to ensure independence between directories.

1.2.4.2 Implementation of Remote Dependencies

In standalone mode, Flutter dependencies direct to the corresponding files in the remote repository. In Flutter mode, the dependency mode remains unchanged.

1) Synchronization of Flutter-related content changes in standalone mode

Considering the complex synchronization process for remote dependencies, Xianyu has developed a series of script tools to automate this process. Assume that Flutter-related content (such as the business source code, engine library, or some resource files) changes. After building is completed in Flutter mode, scripts extract all generated dependency files, replicate them to the remote repository, commit them, and add tags on them. Based on the tags, a new remote dependency description file (such as the podspec file in the iOS directory) is generated. Then, in standalone mode, Flutter dependencies are updated to the latest version. This completes the entire synchronization process, as shown in Figure 1–15.

Image for post
Image for post
Figure 1–15

2) Timing for Synchronization

We recommend that the synchronization script be executed and the app be packaged upon each Flutter business commit during the testing and phased release. During development, data needs to be synchronized once every day.

To ensure project adaptation after Flutter is introduced, Xianyu has extracted Flutter dependencies and stored them to the remote repository for reference by native projects. This ensures the independent and parallel execution of Flutter and native project development.

Several versions of this solution have been implemented by Xianyu and provided to the Flutter team, providing directions and references for subsequent hybrid project organization and planning. This solution is also helpful for the Flutter transformation team. Although solutions vary with projects, the implementation ideas are still worth referencing.

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