How to Use Flutter for Hybrid Development: Alibaba’s Open Source Code Instance

Apps of a certain scale usually have a set of mature and universal fundamental libraries, especially for apps in the Alibaba system, which generally rely on many fundamental libraries in the system. The cost and risk of using Flutter to re-develop an app from scratch are high. Therefore, incremental migration in Native apps is a robust way for Flutter technology to be applied in existing Native apps.

The tech team from Xianyu (闲鱼), Alibaba’s second-hand trading platform, has developed a unique hybrid technology solution in this practice.

Status Quo and Thoughts

We can simply understand this solution in this way: We regard the shared Flutter View as a canvas, and then use a Native container as the logic page. Every time we open a container, we use the communication mechanism to notify the Flutter View to render the current logic page, and then put the Flutter View into the current container.

This solution cannot support multiple horizontal logic pages at the same time, because you must perform operations from the top of the stack during page switching, and cannot perform horizontal switching while maintaining the status. For example, for two pages A and B, B is currently at the top of the stack. To switch to A, you need to pop B out from the top of the stack. At this time, the status of B is lost. If you want to switch back to B, you can only re-open B and the status of the page cannot be maintained.

And, during the pop process, the official Flutter Dialog may be mistakenly killed. In addition, stack-based operations rely on an attribute modification to the Flutter framework, which makes this solution invasive.

FlutterBoost: A New Generation Hybrid Technology Solution

Refactoring Plan

  • Reusable universal hybrid solution
  • Support for more complex hybrid modes, such as support for homepage Tab
  • Non-invasive solution: The solution of modifying Flutter is no longer relied upon
  • Supports for universal page lifecycle
  • Unified and clear design concepts

Similar to the old solution, the new solution still adopts the shared engine mode. The main idea is that a Native container uses messages to drive a Flutter page container, thus achieving the synchronization between the Native container and the Flutter container. We hope that the content rendered by Flutter is driven by the Naitve container.

Simply put, we want to make the Flutter container into a browser. We enter a page address, and then the container manages the page rendering. On the Native side, we only need to consider how to initialize the container, and then set the corresponding page flag of the container.

Main Concepts

Native Layer

  • Container Manager: manager of the container
  • Adaptor: Flutter is the adaptation layer
  • Messaging: Channel-based message communication

Dart Layer

  • Container Manager: To manage Flutter containers and provide APIs, such as Show and Remove.
  • Coordinator: The coordinator that receives Messaging messages and is responsible for calling the status management of the Container Manager.
  • Messaging: Channel-based message communication

Understanding of Pages

Then, when talking about pages in the FlutterBoost, we refer to the Native container and its affiliated widgets. All page routing operations, as well as opening or closing pages, are actually direct operations on Native page containers. No matter where the routing request comes from, it will eventually be forwarded to Native to implement the routing operation. This is also the reason why the Platform protocol needs to be implemented when FlutterBoost is accessed.

On the other hand, we cannot control the service code to push new widgets through the Navigator of the Flutter itself. If the service uses Navigator directly to operate widgets without using FlutterBoost, including non-full screen widgets, such as Dialog, we recommend that the service itself manages its status. This type of widget does not belong to the page defined by FlutterBoost.

Understanding the page concept here is critical to understanding and using FlutterBoost.

Main Differences from the Old Solution

In fact, the Container we introduced is the Navigator, that is, a Native container corresponds to a Navigator. How does this work?

Implementation of Multiple Navigators

Native containers and Flutter containers (Navigators) correspond to each other one by one, and their lifecycles are also synchronized. When a Native container is created, a Flutter container is also created, and they are linked by the same ID. When the Native container is destroyed, the Flutter container is also destroyed. The status of the Flutter container is dependent on the Native container, which is what we call Native-driven. The Manager centrally manages and switches the containers currently displayed on the screen.

Let’s use a simple example to describe the process of creating a new page:

  1. Create a Native container (iOS ViewController, Android Activity or Fragment).
  2. The Native container notifies the Flutter Coordinator through the message mechanism that a new container is created.
  3. The Flutter Container Manager is then notified to create the corresponding Flutter container, and load the corresponding widget page in it.
  4. When the Native container is displayed on the screen, the container sends a message to Flutter Coordinator notifying the ID of the page to be displayed.
  5. The Flutter Container Manager finds the Flutter Container of the corresponding ID, and sets it as a visible container on the foreground.

This is the main logic for creating a new page. Operations, such as destroying and entering the background, are also driven by Native container events.

Officially Proposed Hybrid Solution

How It Works

At most, only one Dart VM can be initialized in a process. However, a process can have multiple Flutter Engines, and multiple Engine instances share the same Dart VM.

Let’s take a look at the specific implementation. Every time a FlutterViewController is initialized on iOS, an engine will be initialized, which means a new thread (theoretically, the thread can be reused) will run the Dart code. Similar effects can be achieved for Activity like Android. If multiple Engine instances are started, note that the Dart VM is still shared, but the code loaded by different Engine instances runs in their independent Isolate.

Official Recommendations

Deep Engine Sharing

However, the official long-term recommendation are not currently well supported.

Multi-Engine Mode

For example, let’s perform the following navigation operations:

Flutter Page1 -> Flutter Page2 -> Native Page1 -> Flutter Page3

We only need to create different Flutter instances in Flutter Page1 and Flutter Page3.

The advantage of this solution is that it is easy to understand and logically sound, but potential problems also exist. If a Native page and a Flutter page are alternated all the time, the number of Flutter Engines increases linearly, and the Flutter Engine itself is a heavy object.

Problems of the Multi-Engine Mode

  • Plug-in registration: Plug-ins rely on Messenger to transmit messages, while Messenger is currently implemented by FlutterViewController (Activity). If you have multiple FlutterViewControllers, the registration and communication of plug-ins will become chaotic and difficult to maintain, and the source and target of message transmission will become uncontrollable.
  • Page differences between Flutter Widget and Native: Flutter pages are widgets, and Native pages are VC. Logically, we want to eliminate the differences between the Flutter page and the Native page. Otherwise, unnecessary complexity appears when we perform page tracking and other unified operations.
  • Increased complexity of communication between pages: If all Dart code runs in the same engine instance and they share an Isolate, a unified programming framework can be used for inter-widget communication. And, multi-engine instances also make this case more complex.

Therefore, we have not adopted the multi-engine hybrid solution in comprehensive consideration.

Summary

From the very beginning of the project, we hoped that FlutterBoost could solve the general problem of Native App hybrid mode access to Flutter. So we have made it a reusable Flutter plug-in, hoping to attract more interested people to participate in the construction of the Flutter community. In this limited space, we shared the experience and code accumulated by Xianyu in the Flutter hybrid technology solution. People who are interested are welcome to actively communicate with us.

Extension and Supplement

Performance

In terms of page rendering performance, the advantages of Flutter AOT are obvious. During fast page switching, Flutter can switch corresponding pages sensitively, logically creating a sense of Flutter with multiple pages.

Support for Release1.0

Access

The Flutter Boost project is already made open source in GitHub. Check it out through this link: https://github.com/alibaba/flutter_boost

(Original article by Chen Jidong)

Original Source

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