Flutter Analysis and Practice: Hybrid Project Transformation
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.
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:
- 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.
- Development Efficiency: The development efficiency is low when development needs to be made in a hybrid environment. Many businesses are developed by using native projects. However, due to the project structure changes, development cannot be made only in the native environment, and adaptation to the Flutter project structure causes unnecessary building for native development when Flutter is integrated. This reduces the development efficiency.
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.
- When a native project is in the Flutter environment (as an iOS or Android subdirectory), whether it can be correctly developed is determined by whether it correctly depends on relevant libraries and files to execute Flutter functions, such as Dart code building, debugging, and hot reload.
1.2.3 Solution Formulation
18.104.22.168 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.
22.214.171.124 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:
App.framework: the Dart business source code file.
Flutter.framework: the Flutter engine library file.
pubs plug-in directory and index files: the Flutter plug-ins for various systems and their custom channels (bridges.)
flutter_assets: the static resources that Flutter depends on, such as fonts and images.
126.96.36.199 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.
1.2.4 Implementation of Transformation
188.8.131.52 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.
184.108.40.206 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.
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.