04 What Are the Key Technical Differences Between Flutter and Other Solutions

04 What Are the Key Technical Differences Between Flutter and Other Solutions #

Hello, I’m Chen Hang.

What is Flutter? What motivated its creation and what pain points does it solve? Compared to other cross-platform technologies, what are the advantages of Flutter? … I believe that many people have similar questions when they first encounter Flutter.

Don’t worry, in this article, I will introduce the historical background and operation mechanism of Flutter to you. Taking the interface rendering process as an example, I will explain its implementation principle, so that you can have a comprehensive understanding and experience of Flutter. After gaining a comprehensive understanding of Flutter, these questions will naturally be resolved.

Next, let’s start with the historical background of Flutter.

Historical Background of Flutter #

For developers who want to develop applications with the same functionality for different operating systems, there are two options:

  1. Use native programming languages ​​(Java and Objective-C) to develop separately for different platforms.
  2. Use cross-platform solutions to develop for different platforms in a unified way.

The native development approach provides the best user experience, but the development efficiency and cost are relatively high. On the other hand, cross-platform development methods have higher development efficiency, but in order to bridge the gap between multiple platforms, the components and APIs exposed by various solutions are much fewer compared to native development. Therefore, the development experience and product functionality are not perfect.

Therefore, the most successful cross-platform development solution is based on the web, leveraging browser controls. The browser ensures that 99% of web requirements can be implemented and there is no need for businesses to “make do” with technology. However, the biggest problem with web-based solutions is the noticeable performance and user experience differences compared to native development, making it less suitable for scenarios that require high user experience.

React Native, which provides a user experience closer to native, has only about 5% of the business support capability that the browser has. It is only suitable for low to medium complexity, low-interaction pages. When faced with slightly more complex interaction and animation requirements, developers need to review and even potentially extend through native code on a case-by-case basis.

These factors have led to the fact that although cross-platform development has been mentioned many times since its inception in the mobile end, it has not been well solved until now.

With these problems in mind, we finally come to the protagonist of this column - Flutter.

Flutter is the SDK for building Google’s Internet of Things operating system, Fuchsia. It is known for being cross-platform, high-fidelity, and high-performance. Developers can use Dart language to develop apps, and a set of code can run simultaneously on iOS and Android platforms. Flutter uses a native engine to render views and provides rich components and interfaces, which undoubtedly provides a good experience for developers and users.

Since the alpha version of Flutter was released by Google in May 2017, to the 1.0 version released at Flutter Live in late 2018, and now the latest 1.5 version (as of July 1, 2019), Flutter is gaining more and more attention.

Many people have begun to feel that cross-platform technology seems to have finally found the best solution. So, let’s take a look at how Flutter solves the problems of existing cross-platform development solutions from a theoretical perspective.

How does Flutter work? #

Unlike most other frameworks used for building mobile applications, Flutter is a complete solution that includes a complete set of underlying rendering logic and an upper-level development language. This not only ensures a high level of consistency in view rendering across Android and iOS (i.e., high fidelity), but also provides a native app-like experience in terms of code execution efficiency and rendering performance (i.e., high performance).

This is the fundamental difference between Flutter and other cross-platform solutions:

  • Frameworks like React Native merely extend the system components through JavaScript virtual machines, with the rendering of components being carried out by the Android and iOS systems.
  • Flutter, on the other hand, completes the entire loop of component rendering by itself.

So, how does Flutter achieve component rendering? To understand this, we need to start with the basic principles of image display.

In a computer system, image display requires the collaboration of the CPU, GPU, and display: the CPU is responsible for image data computation, the GPU is responsible for image data rendering, and the display is responsible for the final image display.

The CPU passes the computed content to the GPU, which renders the image data and puts it into the frame buffer. Subsequently, the video controller reads frame data from the frame buffer at a rate of 60 times per second, based on the vertical sync signal (VSync), and hands it over to the display for image display.

Operating systems follow this mechanism when rendering images, and Flutter, as a cross-platform development framework, also adopts this underlying approach. Below is a more detailed diagram illustrating the rendering principle of Flutter.

Figure 1 Flutter rendering principle

As can be seen, Flutter focuses on how to quickly calculate and synthesize view data between the VSync signals of the two hardware clocks, and then hands it over to the GPU for rendering through Skia: the UI thread uses Dart to build view structure data, which is then composited by the GPU thread, then processed into GPU data by the Skia engine, and finally rendered by the GPU through OpenGL.

Before diving deeper into Flutter, it is necessary to understand the key technologies behind Flutter, namely Skia and Dart.

What is Skia? #

To understand Flutter, you must first understand its underlying graphics rendering engine, Skia. This is because Flutter only focuses on how to provide view data to the GPU, and Skia is a great helper for providing view data to the GPU.

Skia is a high-performance 2D graphics drawing engine developed in C++. Originally a vector drawing software, it was acquired by Google in 2005 and has since been widely used in core products such as Chrome and Android due to its excellent rendering performance. Skia excels in graphics transformation, text rendering, and bitmap rendering, and provides developer-friendly APIs.

Therefore, Flutter, built on top of Skia, has complete cross-platform rendering capabilities. Through deep customization and optimization with Skia, Flutter can maximize platform consistency, improve rendering efficiency, and performance.

With unified low-level rendering capabilities, the upper-level development interfaces and user experience are also unified. Developers no longer need to worry about platform-specific rendering features. In other words, Skia ensures that the same code calling on Android and iOS platforms will have exactly the same rendering effects.

Why Dart? #

Apart from its fast running speed and good execution performance due to its support for both AOT and JIT, there is a debate on why Flutter chose Dart as its programming language instead of JavaScript, which is considered the de facto language for front-end applications.

Many people argue that choosing Dart is a disadvantage for the promotion of Flutter, as it adds an extra layer of barrier to learn a new language. Just think about Java for Android or JavaScript for Node.js, things might have been different if a different language was chosen.

However, the reason given by Google for choosing Dart is simple and straightforward: the Dart language development team is right next door. They can quickly implement the language features required by Flutter at the syntax level. If JavaScript were chosen, it would have to go through various committees and lengthy decision-making processes involving browser vendors.

In fact, Flutter has indeed received strong support from its sibling team. Each release of Dart, such as Dart 2.0 in February 2018, Dart 2.1 in December 2018, Dart 2.2 in February 2019, and Dart 2.3 in May 2019, included many customizations tailored for Flutter (e.g., improved AOT performance and smarter type implicit conversions).

Of course, I believe there are more convincing reasons why Google chose Dart as the development language for Flutter:

  1. Dart supports both JIT and AOT compilation. During development, using JIT results in an exceptionally short development cycle and disrupts the conventional debugging process (supports stateful hot reload). During release, using AOT leads to more efficient execution of native code, resulting in better code performance and user experience.
  2. Dart is a modern language that combines the strengths of many other excellent programming languages (e.g., a well-developed package management mechanism). It is also for this reason that the learning curve for Dart is not high, making it easy to pick up.
  3. Dart avoids preemptive scheduling and shared memory, allowing object allocation and garbage collection to be performed without locks. It performs quite well in terms of performance.

Dart is an excellent modern language initially designed to replace JavaScript as the official language for web development. Considering the strong competition, one can easily guess the outcome. This is also why Dart’s ecosystem is less vibrant compared to other popular languages.

With the release of Flutter, Dart began to transition, and its own positioning also changed. It is now focusing on improving the experience of building client applications. As a result, more and more developers are gradually learning and understanding this language, and together they are improving its ecosystem. With the booming momentum of Flutter and the powerful business operation capabilities of Google, I believe the future of Dart after the transition will be very promising.

Principles of Flutter #

After understanding the basic operating mechanism of Flutter, let’s take a deeper look at the implementation principles of Flutter.

First, let’s take a look at the architecture diagram of Flutter. I hope that through this diagram and its corresponding explanation, you can establish an overall impression of Flutter when you start learning, so that you can understand the key differences between Flutter and other cross-platform solutions from the perspective of framework design and implementation principles. This will lay a solid foundation for your learning, rather than getting stuck in the details of language and framework functionalities.

Flutter Architecture Diagram

Figure 2 Flutter Architecture Diagram

Note: This diagram is from Flutter System Overview

The Flutter architecture is designed with a layered approach, consisting of three layers from bottom to top: Embedder, Engine, and Framework.

  • The Embedder is the platform adaptation layer, implementing the adaptation of platform-related features such as rendering surface settings, threading settings, and platform plugins. From here, we can see that Flutter has relatively few platform-specific features, making it easier to maintain cross-platform consistency at the framework level.
  • The Engine layer mainly consists of Skia, Dart, and Text, and implements Flutter’s rendering engine, text layout, event handling, and Dart runtime capabilities. Skia and Text provide the upper layers with the ability to call the underlying rendering and layout capabilities, while Dart provides the ability for Flutter to call Dart and the rendering engine at runtime. The role of the Engine layer is to combine them and implement view rendering based on the data they generate.
  • The Framework layer is a UI SDK implemented in Dart, which includes features such as animation, graphics drawing, and gesture recognition. In order to provide a more intuitive and convenient interface for drawing fixed-style graphics such as control widgets, Flutter also provides a UI component library based on these basic capabilities, according to the Material and Cupertino visual design styles. When developing with Flutter, we can directly use these component libraries.

Next, let me use the interface rendering process as an example to explain how Flutter works.

The various interface elements (widgets) in the page are organized in the form of a tree, called the widget tree. Flutter creates different types of rendering objects for each widget in the widget tree, forming a rendering object tree. The process of displaying the rendering object tree in Flutter is divided into four stages: layout, painting, composition, and rendering.

Layout #

Flutter adopts a depth-first mechanism to traverse the rendering object tree, determining the position and size of each rendering object in the rendering object tree on the screen. During the layout process, each rendering object in the rendering object tree receives layout constraint parameters from its parent object, determines its own size, and then the parent object determines the position of each child object based on the logic of the widget, completing the layout process.

Flutter Layout Process

Figure 3 Flutter Layout Process

To prevent the whole widget tree from being re-laid out when a factor node changes, Flutter introduces a mechanism called a layout boundary, which can automatically or manually set a layout boundary for certain nodes. When any object within the boundary undergoes a layout change, it will not affect objects outside the boundary, and vice versa.

Flutter Layout Boundary

Figure 4 Flutter Layout Boundary

Painting #

After the layout is completed, each node in the rendering object tree has a clear size and position. Flutter paints all the rendering objects onto different layers. Like the layout process, the painting process also uses a depth-first traversal approach, always painting itself first before its children.

Take the following diagram as an example: Node 1 is painted first, then Node 2, followed by its children Node 3, 4, and 5, and finally Node 6.

Flutter Painting Example

Figure 5 Flutter Painting Example

As can be seen, due to certain reasons (such as manual view merging), Node 5 of Node 2 is at the same level as its sibling Node 6, which would cause unnecessary repainting of Node 6 when Node 2 needs to be repainted, resulting in performance loss.

To solve this problem, Flutter proposes a mechanism called a repaint boundary, corresponding to the layout boundary. Within a repaint boundary, Flutter switches to a new layer, which avoids mutual interference between objects inside and outside the boundary and prevents unnecessary repainting of unrelated content.

Flutter Repaint Boundary

Figure 6 Flutter Repaint Boundary

A typical scenario for using repaint boundaries is ScrollView. When the ScrollView is scrolled, the view content needs to be refreshed, triggering content repainting. In general, other content does not need to be repainted at this time, and this is where repaint boundaries come into play.

Composition and Rendering #

As the complexity of terminal device screens continues to increase, the rendering tree hierarchy in Flutter is usually large. Directly delivering the rendering tree to the rendering engine for multi-layer rendering may result in a large amount of redundant rendering. Therefore, a composition step is required before rendering, which calculates the final display effect of all layers based on factors such as size, hierarchy, transparency, etc., classifies and merges similar layers, simplifies the rendering tree, and improves rendering efficiency.

After the merging is completed, Flutter processes the geometric layer data into 2D image data using the Skia engine, and finally renders it using the GPU, completing the display of the interface. I have already explained this part in the previous content, so I won’t repeat it here.

Next, let’s take a look at what knowledge we need to learn when studying Flutter.

What knowledge do you need to master to learn Flutter? #

With the increasing fragmentation of terminal devices and the need for support for multiple operating systems, cross-platform development is definitely the trend of the future front-end. We should embrace the change. Flutter provides a complete mobile cross-platform solution and indeed fills the gaps in the current cross-platform development frameworks, solving the pain points in the industry. It is very likely to become the ultimate solution in the field of cross-platform development, and has a very bright future.

So, what knowledge do we need to master to learn Flutter?

I have divided the Flutter technology stack according to the development process of an app (development, debugging and testing, release and online operations), which includes almost all the knowledge points needed for Flutter development. And I will explain these knowledge points one by one in this column. Once you have mastered these knowledge points, you will have the necessary skills for enterprise application development.

These knowledge points are shown in the following figure:

Figure 7 Flutter knowledge system

With this figure, do you feel that the learning path for Flutter has become clearer?

Conclusion #

Today, I have introduced you to the historical background and operating mechanism of Flutter. Taking the interface rendering process as an example, I have explained the implementation principles of Flutter from four stages: layout, painting, composition, and rendering. In addition, I have introduced you to the key technologies that build the underlying framework of Flutter: Skia and Dart. They are the core of Flutter that sets it apart from other cross-platform development solutions.

Finally, I have presented a Flutter learning mind map, which covers various knowledge points related to Flutter around the iteration cycle of an application. I hope that through this column, I can clarify the design principles and knowledge system behind Flutter, so that you can have a holistic understanding of Flutter. This way, after completing this column, you will have the theoretical foundation and practical experience for developing enterprise-level applications.

Thought-provoking questions #

How do you understand the three main features of Flutter: cross-platform, high-fidelity, and high performance? How do you plan to learn this column?

Feel free to leave a comment in the comment section to share your views, and I will be waiting for you in the next article! Thank you for listening, and feel free to share this article with more friends to read together.