Breakdown! A Detailed Comparison Between the Flutter Widget and CSS in Terms of Layout Principles
By Zhang Han (Menliu), Taobao Technology Department, Alibaba New Retail
In my previous article titled, How to Remove Obstacles and Connect Flutter to the Web Ecosystem?, I mentioned how “the connection between CSS and the Flutter Widget is a tedious process and involves completeness problems.” I gave the conclusion but did not give any reasons.
Now, I want to pick up where I left off. This article compares the differences between the layout principles of the Flutter Widget and CSS and shares the problems and solutions encountered in the connecting process.. The article is broken down into four parts:
- Introduction to CSS
- Introduction to the Flutter Widget
- Let’s Battle! Comparison from Five Angles
- Love and Peace: Feasibility of Connection
CSS is the abbreviation of cascading style sheets. It is a markup language used to describe styles. The original idea was born in 1994 and the first version was published in 1996. HTML describes the structure of web pages, while CSS describes the representation of web pages. This couple has been collaborating for 20 to 30 years and is still the most efficient among layout tools.
CSS is Awesome
There is no doubt that CSS is very efficient in describing the layout. Subsequent layout solutions, such as the CSS in JS frontend framework, XML description files, and Flutter, were deeply influenced by CSS even if they did not directly copy the features of CSS.
The rendering process of a CSS file in a browser includes four steps: loading, parsing, querying and applying the CSS file to DOM nodes, and calculating the layout. First, the browser first the HTML file and parses it.
Then, the browser generates a layout tree based on the DOM tree with ComputedStyle. The display characteristics of the nodes are different, and the types of generated LayoutObjects are also different. Then, the layout algorithm will traverse the layout tree multiple times and calculate the Rect of each node. This process is very complicated. A DOM node does not necessarily correspond to a layout node. We need to consider factors, such as display:none, pseudo element, text nodes, and shadow dom. The whole process is synchronous. After the parsed DOM node has been laid out, if the browser parses another
<style> tag, it will match the CSS style in this tag, apply the new style to the parsed DOM node, and calculate the layout all over again.
After the layout is complete, Paint will run. The layout information is committed to the compositor thread layer by layer, and then divided into pieces and delivered to the GPU thread for drawing. I think the last two steps are relatively fast. Layout and Paint in the main thread are the most time-consuming and are mixed with the execution of JS code.
Unlike CSS, the Flutter Widget is well-crafted with clear classification and features. Its various properties are not coupled and do not affect each other. The design of widgets is relatively atomized, and they almost do not affect each other. Moreover, the nesting mode of widgets must meet certain requirements, so the efficiency of layout algorithms can be maintained.
The design of Flutter is relatively reasonable. This, to a large extent, is due to the link between Google’s Flutter Development Team and Chrome. Some members of that team have been participating in the formulation of CSS specifications for many years. They are well-experienced in this field. On the whole, the new framework does not have much redundancy or bear any burdens of the past as CSS does, so it can become easier to use based on predecessors. In other words, if CSS designers could put aside burdens of the past to redesign CSS without considering backward compatibility, CSS would likely be as good as the current Flutter Widget.
Regarding the rendering process of Flutter, the official document entitled, How Flutter works, is good learning material. The biggest highlight is the sub-linear layout, which has performance close to O(n) and is much more efficient than CSS.
The preceding figure illustrates the process. You can go to the official website to learn more. The focus of this article is the battle.
Round 1: Supportive Forces
Before we start to compare CSS and the Flutter Widget, let’s take a look at which organizations support and operate them.
Behind CSS is the W3C, which is an industry-recognized standardization organization. CSS has been implemented by popular browsers, and browser vendors are also actively promoting the development of standards. CSS is an open technology. The big names behind it are a series of for-profit or nonprofit organizations, such as the W3C, Chrome, Safari, and Firefox. They are mutually beneficial and develop together.
However, only Google is behind Flutter. Flutter is open-source, but the design and implementation are led by the Google team, and very few people have the ability and opportunity to participate in the development of Flutter. PR only does small repairs. One difference between the framework and the standard lies in backward compatibility. It is common for the framework to launch an updated version with awesome optimizations and changes, but without backward compatibility.
From this perspective, CSS is a standardized technology with tenacious vitality, though it is bloated. The CSS code you write now can still run five years later, whereas the Flutter code you write now may or may not run five years later. If Google announced that it would not maintain Flutter anymore, the community would likely lose confidence in an instant, and Flutter would die. If Chrome announced that it would not support CSS, CSS would still be alive, but Chrome support would likely die (see IE as an example), and Firefox would be happy.
Round 2: Learning Costs
When it comes to the cost of learning, CSS is efficient. Let’s elaborate on a few cases.
Training classes, such as “From a Newbie to a Frontend Master in Three Months,” in the market prove the low-cost of frontend learning. We all know the title is wrong. Trainees cannot become frontend masters in only three months. All they can learn is to use CSS to crop pages. However, there are no training classes like “Master Flutter in Three Months.” It is possible for skilled frontend developers to learn Flutter in three months or less (and faster if they are client developers.) However, this is impossible for trainees that have no base knowledge of programming. These trainees will probably ask for a refund after they attend the classes.
WeChat held a programming course entitled, *”Creative Camp of WeChat Applet Programming for Children and Teenagers.” The UI of WeChat applets is written with limited HTML and CSS. If Flutter wants to hold similar competitions, it has to be oriented to people that have programming experience.
From the perspective of syntax, CSS is easy to learn because it is only a descriptive language and does not involve complex programming logic. The design goal of CSS is to describe “what I want the UI to be,” or in other words, a result-oriented description. By contrast, widgets are implemented by Dart code. For widgets, the UI is written together with the code logic, and “How Do I Combine and Form the UI” is a process-oriented description based on the line-by-line description of code. Therefore, CSS is more intuitive, and the code written in CSS is easier to understand. There is also a small reason. Layers of nested code, such as Flutter widgets, are very troublesome to write and modify. It is too dependent on the editor and inconvenient to copy and paste (sometimes you copy the code.)
CSS has been around for years, so plenty of learning materials are available. CSS specifications are detailed. Websites, such as MDN, CSS Tricks, and CodePen, provide enough resources to learn. There are also online training materials that provide clear guidance on the knowledge framework and learning path. By contrast, the Flutter Widget only has its official website, and its learning materials and community ecosystem are still far behind.
Round 3: Development Efficiency
It is easy to get started with CSS, but it is difficult to master. People without several years of development experience are unlikely to master it.
If you want to draw a learning curve of CSS, it must be a rapid rise at the initial stage. When it reaches a certain height, it will slow down or fall. However, when you start to learn Flutter, you must understand many concepts and change the way you think. Getting started is a slow process, but as you learn more, you will also learn faster. Writing CSS is like operating a marionette while writing a widget is like building with Lego blocks.
Various properties of CSS can affect each other, and the input and output are not simply corresponding. If you write width: 100px, the width may not be 100 pixels. This is like operating a puppet with hundreds of tied lines. You want to pull five lines to make the “OK” gesture, but when you pull one of the lines, you may move the whole upper body instead of just one part.
The Flutter Widget is more atomized and has requirements on what can be nested. This is like building with Lego blocks. Each block is small but has a clear type of bayonet. You must conform to the design, and then unleash your creativity to assemble the blocks into various shapes. Flutter compels you to combine widgets in the ideal way to avoid writing code with poor performance. However, in CSS, a property can be written with any other properties, and the standard can always give you a reasonable explanation. Therefore, the code written is very confusing, which increases the difficulty of the layout.
As such, the development efficiency of CSS is relatively fast in the early stages, but accumulation is difficult, and large-scale collaboration is also difficult to manage (high coupling and global scope.) By contrast, Flutter has better encapsulation, which is conducive to collaboration, and the development efficiency becomes increasingly high.
Round 4: Speed is Everything
The Flutter Widget and CSS, which is faster? Everyone agrees that the former is faster. I think that Flutter has two major performance advantages in layout over CSS: one is the sub-linear layout algorithm, and the other is a more reasonable threading model.
The layout principle of the Flutter Widget is more efficient than CSS, which sacrifices some flexibility. When we expand the Flutter Widget in the future, we must follow these designs so the Flutter Widget can remain efficient. Complex details lie behind the simple properties of CSS. This makes the layout model increasingly complex and the rendering pipeline increasingly long. The display alone has a dozen or two dozen values, each of which has a great influence on the layout. This is convenient for developers but challenges the layout performance.
The threading model is also a performance bottleneck that browsers have been criticized for. The main thread is too busy, which has to deal with JS execution, HTML and CSS parsing, DOM construction, and layout calculation. By contrast, the four threads in Flutter are more balanced. The GPU thread does the same job as a browser. The code of the host platform (Android or iOS) runs in the Platform thread, the Flutter framework mainly runs in the UI thread, and the IO thread implements the loading of images, fonts, and other files from the network.
Round 5: Future Development
Let me share some data on the current status of CSS. The W3C has defined 520 CSS styles, and 703 styles are found on the Chrome platform, including some prefixed styles. The usage of many styles is very low. The Alipay applet personnel summarized 184 different CSS styles used in the top 100 applets.
To sum up, 520 styles are defined in the W3C standard, and Chrome supports more than 180 private styles, but no more than 200 styles are commonly used.
CSS suffers many burdens of the past. I also feel that I rarely use most styles, some of which are prohibited in the best practices. After you learn 50 CSS styles, you can write 80% of layouts. For difficult styles, you can add a few more labels and write JS code to implement them. However, we cannot abandon these styles. We must support them. When a useful property is added, its compatibility with all existing properties must be clarified.
CSS is advancing with heavy burdens and is destined to become increasingly complex, whereas the Flutter Widget is light-packed, easy to decouple, pluggable, and combinable. If you want to abandon some widgets in the future, remove these widgets from the main package and put them in an independent plug-in. When you want to use them, import them from the plug-in. The burdens of the past have limited impact on the entire iteration process.
Love and Peace: The Feasibility of Connection
The battle between CSS and the Flutter Widget has ended, and there is no loser. Now, let’s talk about love and peace between them.
The linear layout of Flutter is attractive, while the flexibility of CSS is popular. Can we take their advantages and discard their disadvantages, so developers can write CSS while using Flutter for rendering in the underlying layer? Many people have thought about this, so there are many solutions to connect the frontend framework or applets to Flutter. When you try to implement this, you will encounter the problem of how to convert CSS to Widget.
Technically, the connection is feasible. In my previous article entitled, “How to Remove Obstacles and Connect Flutter to the Web Ecosystem?”, I introduced different implementation strategies, wrote code to connect them (using C++ code to hack), and ran through the entire rendering process.
I would like to introduce my way of implementation, which can be divided into three steps:
1. Parse CSS Syntax
I wrote a lite version of the CSS Object Model (CSSOM) as a subset of the standard CSSOM. I use this lite version to create style sheets and style properties, add, delete, query, and change styles, parse and calculate style values, and query and match selectors. This function is independent. Take it if you need it.
CSSOM is mainly used to process the upper-layer syntax of CSS and convert it into a consistent data format to prepare for the next conversion. A part of CSS syntax is implemented in this process, such as CSS selectors. This part of syntax includes pseudo-class selectors, selector relationships, and features, such as @media and @keyframes. CSS variables and calc() can also be implemented. Whether CSS or CSS in JS is written directly in the upper layer, the input data format for the next conversion must be consistent, which can simplify subsequent implementations.
2. Map CSS Properties to the Data Format of the Flutter Widget
This step is to convert the basic data format of CSS into the data format on which the Flutter Widget depends. For example, the color property of CSS is converted to the Color class of Flutter, margin and padding are converted to the EdgeInsets class, flex-direction is converted to the Axis enumeration, and text-related properties are converted to the TextStyle class.
The conversion is atomized, which is simple since it is only data format conversion. However, it is necessary to understand the design of CSS and the Flutter Widget, correspondence of the same concepts between the semantics of CSS and the Flutter Widget, and relevant technical details. According to my experience, if this correspondence is converted incorrectly, the subsequent layout will not be adjusted correctly.
3. Build a Widget Tree
After you get the data from CSSOM and master the semantic transformation of CSS and the Flutter Widget data structures, you can combine the structure defined by HTML to generate a real widget tree.
When you build a widget tree, you cannot only consider CSS. The combination of HTML and CSS is equivalent to the Flutter Widget. You also need to deal with the relationship of different types of HTML nodes with the Widget. The layout styles of the nodes are different, so the generated nodes are also different. The hierarchy depth of the Flutter Widget is deeper than that of HTML. For example, if a common div tag only contains a common box model style, it is converted to Container. If it contains flex-related properties, it is converted to Flex, Center, Row, or Column, depending on the specific configuration. If it includes absolute positioning, it is converted to Positioned or Stack. This process is also very complicated and contains a lot of details. You must understand the semantics of default HTML tags, CSS layer models, and block formatting contexts (BFCs). It is also necessary to understand the nesting restrictions between Flutter widgets to combine them and achieve the ideal effect for CSS.
For the problems mentioned at the beginning, the preceding analysis of technical feasibility responds to the problem of “complicated.” The following content discusses “completeness.”
CSS is flexible whereas the Flutter Widget is restricted. The conversion of a flexible syntax into a restricted implementation is doomed to be incomplete.
In CSS, a property can be written with any other properties. The W3C standard always provides a clear explanation. HTML and CSS are error-free, even though they do not meet expectations. However, Flutter has clear restrictions on which widgets can be placed in a widget. For example, a Stack must be placed on the Positioned outer layer, and only one child widget can be placed in the Center. Errors are reported for nesting that does not meet expectations, so code will not be used in a mixed manner, and the layout algorithm can work fast. From this perspective, the complexity of CSS is O(n!), whereas the Flutter Widget involves polynomial complexity. Therefore, using the Flutter Widget to implement CSS is doomed to be incomplete. (Is this a P and NP problem?)
If CSS writing modes are restricted, can CSS be connected to Widget implementation? This is feasible.
If you want to use the sub-linear layout of the Flutter Widget, you must sacrifice some flexibility of CSS. If you want to break through this restriction with technology, you must change the sub-linear layout algorithm. After the modification, Flutter is no longer Flutter and loses its performance advantages.
According to my experience, the Flutter Widget can only support CSS styles within a certain range. Its restriction on CSS does not lie in the number of styles or the implementation of a certain style, but in the mixed use of styles. Even if the Flutter Widget supports 500 CSS styles, some properties still cannot be used at the same time. It is invalid to use style A in the outer layer and style B in the inner layer. Only C is valid if C and D are written together. I estimate that the scope of the Flutter Widget’s support for CSS is wider than the current ReactNative and Weex and can meet the needs of most businesses and applets. However, business requirements will increase. After the maximum scope of support is reached, the scope is difficult to expand. As such, developers need to be trained. This will affect their development experience.