43 How to Build Your Own Flutter Hybrid Development Framework I

43 How to Build Your Own Flutter Hybrid Development Framework I #

Hello, I’m Chen Hang. In the final topic of this course, I will talk to you about how to design your own Flutter hybrid development framework.

Hybrid development refers to embedding the Flutter runtime environment into the native app project while continuing to use the native technology stack for the overall architecture of the app: the native developers provide the host container and basic capabilities for Flutter to run, while the Flutter developers are responsible for the application’s business logic and most rendering work within the app.

The benefits of this development model are quite obvious. For engineers, the cross-platform Flutter framework reduces reliance on the underlying environment and isolates the differences between various terminal systems using a complete technology stack and toolchain. Whether it is Android, iOS, or even frontend engineers, they can use unified and standardized capabilities for business development, thereby expanding their skillset. For companies, this approach not only provides a native app with a good user experience and rich underlying capabilities but also has the advantages of low-cost development with cross-platform technology and consistent multi-end experiences, directly saving development resources.

So, when introducing Flutter hybrid development capabilities into a native project, how should we design the engineering architecture, and what are the work patterns for native development and Flutter development?

Next, in today’s sharing, I will focus on introducing the design ideas and development directions for these two topics. In the next sharing session, I will explain in detail the technical details that need to be considered when implementing the business using an actual case. This way, you will have a guide when designing your own hybrid development framework for your native project.

Hybrid Development Architecture #

In the 41st article, I introduced to you two means of software function partitioning, namely componentization and platformization, as well as how to classify and aggregate software functions in a layered manner while satisfying the principle of one-way dependency. These design ideas can help us reduce the complexity of the overall project and improve the scalability and maintainability of the app when designing software system architecture.

Unlike pure Flutter projects which can split software functions and manage project dependencies in an autonomous manner, the function partitioning of Flutter hybrid projects requires the cooperation of native projects and Flutter projects. In the perspective of a Flutter module, part of the rendering-related basic capabilities are fully implemented by Flutter code, while the other part, which involves operating system-level, generic business capabilities, and overall application architecture support, needs support from the native project.

In the 41st article, we used the four-quadrant analysis method to categorize pure Flutter applications into four types based on business and UI. Similarly, the functional units of hybrid projects can also be divided into four dimensions according to this partitioning logic: native basic functions without business attributes, native UI controls without business attributes, native basic business functions without UI attributes, and independent business modules with UI attributes.

Figure 1 Four-quadrant analysis method

From the diagram, we can see that there is no difference between pure Flutter projects and hybrid projects in the definition of the first three dimensions (i.e., native UI controls, native basic functions, native basic business functions), except that the implementation method has changed from Flutter to native. As for the functional ownership of the fourth dimension (i.e., independent business modules), considering that the smallest unit of a business module is a page, and Flutter ultimately presents itself as an independent page, so we also classify Flutter modules into this category. Our project can directly depend on it like native business modules and provide users with independent business functions.

By dividing these components and their dependencies from top to bottom, we have a complete hybrid development architecture. It can be seen that the boundaries of the native project and the Flutter project are well-defined, and both sides can maintain their original layered dependency management development mode.

Figure 2 Flutter hybrid development architecture

It should be noted that as a functional component embedded in a native project, the runtime environment of the Flutter module is supported by the native project. This means that basic functions outside of rendering and interaction capabilities (such as networking and storage), as well as business common capabilities shared with native business (such as payment and accounts), need to be completed with the cooperation of the native project. In other words, the native project provides upper-level calling interfaces in a layered manner, and the Flutter module directly accesses the native code host’s corresponding functional implementation in the form of plugins.

Therefore, not only do native components with different ownership definitions have a layered dependency relationship, there is also an implicit layered dependency relationship between the Flutter module and the native components. For example, the account plugin in the Flutter module, which belongs to the basic business module, depends on the account functionality in the native basic business module, and the network plugin in the Flutter module, which also belongs to the basic business module, depends on the network engine in the native basic functions.

In the hybrid project architecture, the dependency management behavior across technology stacks, such as the native project depending on the Flutter module and the Flutter module depending on the native project, is actually achieved by abstracting both parties as dependencies of each other’s corresponding technology stack, thereby achieving layered management: that is, abstracting the native dependency on Flutter as a dependency on native components encapsulated by the Flutter module, and abstracting the Flutter dependency on native as a dependency on native behaviors encapsulated by plugins.

Flutter Hybrid Development Workflow #

For software development, the responsibilities of engineers involve the entire lifecycle from requirements to deployment, including requirement phase -> solution phase -> development phase -> release phase -> online operation and maintenance phase. It can be seen that this is actually an abstract workflow.

Among them, the most closely related to engineering is the development phase and release phase. We abstract the parts of the workflow that are closely related to engineering and define it as the development workflow. Based on the key nodes and high-frequency nodes in the lifecycle, the entire workflow can be divided into the following seven stages: initialization -> development/debugging -> build -> testing -> release -> integration -> native toolchain:

Figure 3 Flutter Hybrid Development Workflow

The first six stages are the standard workflow of Flutter, and the last stage is the standard workflow of native development.

It can be seen that in the hybrid development mode, there is a clear division of labor between the development mode of Flutter and the development mode of native development: the Flutter module is upstream of the native project, and its final product is the dependency of the native project. From the perspective of the native project, its development mode is no different from a normal native application, so it will not be repeated here. We will focus on the Flutter development mode.

For the six stages of the standard workflow of Flutter, each stage will involve specific requirements proposed by business or product features, the selection of technical solutions, the measurement of the work cost, availability, and reliability of each stage, as well as the access and configuration of monitoring-related basic services.

Every task follows a fixed set of steps, and as the documentation, code, and requirements increase with the development scale, we will find that there are more and more repetitive steps. At this time, if we can abstract these steps like abstract code, we can greatly improve development efficiency.

Excellent programmers will discover problems in their work and explore ways to improve productivity from them, and changing the mindset is a good starting point. Looking at these problems from the perspective of continuous delivery, we hope that the overall solution can be in a repeatable and configurable form to ensure the development experience, efficiency, stability, and reliability of the entire workflow, and all of this depends on Flutter’s support for command-line tools.

For example, for Dart code analysis during the testing phase, we can use the “flutter analyze” command to check for possible syntax or semantic issues in the code; for example, in the package publishing phase, we can use the “flutter packages pub publish –dry-run” command to check the package to be published before publishing and use the “publish” command without the “dry-run” parameter to submit the package to the Pub site after confirming it is correct.

These basic commands abstract the inputs, outputs, and execution process of each development node. By mastering them and the corresponding extension parameter usage, we can not only create an easy-to-use and convenient engineering development environment for local development, but also deploy these commands to the cloud to achieve automation of project building and deployment.

I have summarized the key commands involved in these six stages into a table. You can use this table to understand the Flutter standard workflow implemented in practice.

Table 1 Flutter Standard Workflow Commands

Conclusion #

For Flutter hybrid development, it is important to define clear boundaries between the native and Flutter components in terms of project architecture and workflow.

At the architectural level, the Flutter module should be defined as an independent business layer in the native project. The native basic business layer should provide common and basic capabilities to the Flutter module, while the native basic functionality layer should provide fundamental support. This layering approach helps manage dependencies.

At the workflow level, the development of the Flutter module, which is upstream of the native project, should be abstracted as a project management of native dependencies. A corresponding workflow should be extracted to manage each stage in a repeatable and configurable command-line manner.

By introducing the Flutter runtime environment into the native app project, and adopting a collaborative approach where the native developer focuses on application architecture and basic capabilities while the Flutter developer focuses on application-level business, we can leverage the strengths of both the native app and the Flutter framework. This not only saves development resources but also aligns with the development trend of the industry’s talent capabilities.

Thought-provoking question #

In addition to project dependencies, we also need to manage the dependencies of the Flutter SDK itself. Considering that the Flutter SDK updates very frequently, how can we ensure that every person in a team collaboration environment uses the exact same version of the Flutter SDK?

Please leave a comment in the comment section to share your viewpoint, and I will wait for you in the next article! Thank you for listening, and feel free to share this article with more friends for reading together.