Detailed Flutter framework

single page application

To understand cross-platform frameworks, you must first know that most mobile cross-platform frameworks are "single-page" applications.

What is a "single page" application?
That is to say, for native Android and iOS, the entire cross-platform UI runs on one by default. Activity/ ViewControllerby default, there will only be Activityone ViewController. , ReactNative, Weex, and Ionic are all like this by default, so in general, the routing of the framework is not directly related to the native routing.

Cross-platform applications are single-page applications by default, and their routing stack is incompatible with the native layer.

Of course, a word is repeated here: "default" , that is, it can actually support custom hybrid stacks, such as the officialFlutterEngineGroup​​​​​​ , third-party framework flutter_boost​​​​​​, mix_stack​​​​​​​​, flutter_thrio​​Wait.

The Design Philosophy of a Cross-Platform Framework

The development on the end is nothing more than three things, "data acquisition" , "state management" , and "page rendering" .
The competition in the cross-end field is the competition of "virtual machine" , "rendering engine" , "native interaction" and "development environment"

Flutter uses the Dart VM, and Dart supports both JIT and AOT compilation modes.
  • Use JIT compilation in the development stage to realize hot update preview, dynamic loading, etc.,
  • The release phase uses AOT mode to compile into machine code to ensure startup speed and cross-terminal information transmission efficiency.
Flutter uses the Skia rendering engine for view rendering,
  • Avoid the differences in control rendering on different platforms.
  • Without this layer of interaction, the efficiency is also improved.
In terms of native interaction, Flutter is very efficient in interacting with native.
  • Dart itself is cross-platform, and the underlying C++ can directly access the native API.
  • Plus information is delivered using machine code (BinaryMessage)
The RN virtual machine uses JSC (Javascript Core) to perform calculations
  • In the early architecture, the virtual machine used JSC (Javascript Core) to perform calculations, so that it can fully reuse the JS ecosystem and attract a large number of front-end developers to participate.
RN directly reuses the original rendering channel on the rendering engine, without directly using WebKit or other web engines,
  • Because the computing consumption brought by the previous Web when building complex pages is far inferior to the rendering of pure native engines. So it directly reuses the original rendering channel, so that it can bring an experience that is almost the same as the original.
  • Although the early RN architecture made full use of the existing ecology, after all, it is not like Flutter from the beginning to the end, so it is so bottomless. The problem is that at the layer from JSC to native rendering, a lot of Bridges are used, and information is passed back and forth in multiple threads through JSON serialization. Such consumption may not be obvious in the simple interaction process, but There will be obvious lag in a large number of interactions and renderings, which has also become a widely criticized point.

Architecture of the cross-platform framework

Flutter Core Architecture

insert image description here

Every component in the framework layer is optional and replaceable. As can be seen from the figure above, the Flutter system can be divided into three layers. The upper framework (Framework), the middle engine (Engine), and the underlying embedded layer (Embedder).

Flutter Framework

  • Framework: The Flutter framework framework layer is a responsive framework implemented in pure dart language, consisting of many abstract levels. At the top of these layers are the Material and Cupertino Widgets that we use frequently. We use these two types of Widgets in most cases. For example: Basic Widgets such as UI/text/picture/button. Below the Widget layer, you'll find the Rendering layer. The Rendering layer simplifies the layout and drawing process. It is an abstraction of dart:ui. dart:ui is the bottom layer of the framework, which is responsible for handling the communication with the Engine layer. The core code of this part is: the flutter package under the flutter warehouse, and the io, async, ui (dart:ui library provides the interface between the Flutter framework and the engine) and other packages under the sky_engine warehouse.

    • Typically, developers interact with Flutter through the Flutter Framework, which provides a modern, reactive framework written in the Dart language. It includes a rich set of platforms, layouts, and base libraries, organized into a series of layers. From bottom to top there are:
    • Foundation classes and component services, such as animation, painting, and gestures, provide common abstractions on top of the underlying foundation.
    • The rendering layer provides an abstraction for handling layout. Through this layer, you can build a tree of renderable objects. You can manipulate these objects dynamically and the tree will automatically update the layout to reflect your changes.
    • The widgets layer is a compositional abstraction. Each render object in the render layer has a corresponding class in the widgets layer. Additionally, the widgets layer allows you to define combinations of classes that can be reused. This is the layer that introduces the reactive programming model.
    • The Material and Cupertino libraries provide a comprehensive set of controls that implement the Material or iOS design language using composition primitives at the widget layer.

The Flutter framework is relatively small; many of the higher-level features a developer might use are implemented as packages, including platform plugins like cameras and webviews, and platform-independent ones like characters, http, and animations features, which are built on top of the core Dart and Flutter libraries. Some of these packages come from the broader ecosystem, covering services like in-app payments, Apple authentication, and animations.

dart:ui library

The dart:ui library exposes the lowest-level services that are used to bootstrap the Application, such as driving input, drawing text, layout, and rendering subsystems.

So you can build a Flutter App just by instantiating classes from the dart:ui library such as Canvas, Paint, and TextField. But if you are familiar with drawing directly on the canvas, you will know that it is difficult and cumbersome to use these low-level APIs to draw a pattern. Next let's consider something that isn't drawing, such as layout and hit testing. What do these mean? This means you have to manually calculate all the coordinates used in your layout, and then mix in some drawing and hit testing to capture user input. Do the above for each frame and track them. This method is more suitable for those relatively simple APPs, such as one that displays text in a blue area. If it is enough for you for those more complex apps or simple games. Not to mention animations, scrolling, and some cool UI effects that product managers love.

Rendering library

Flutter's Rendering tree (rendering tree). The hierarchical structure of RenderObject is used by the Flutter Widgets library to implement its layout and background drawing. Generally speaking, although you may use RenderBox to implement custom effects in your application, in most cases the only interaction we have with RenderObject is when debugging layout information.

The Rendering library is the first abstraction layer on top of the dart:ui library. It does all the heavy math for you (like keeping track of coordinates that need to be constantly calculated). It uses RenderObjects to handle these tasks. You can think of RenderObjects as the engine of a car, it does all the work of displaying your app to the screen. All RenderObjects in the Rendering tree will be layered and drawn by Flutter. In order to optimize this complex process, Flutter uses an intelligent algorithm to cache these instantiated objects that consume performance to achieve performance optimization. In most cases, you will find that Flutter uses RenderBox instead of RenderObject. This is because the builders of the project found that using a simple and box layout constraint was successful in building an efficient and stable UI. Imagine that all Widgets are placed in their boxes. The relevant parameters in this box are calculated, and then placed in the middle of other organized boxes. So if only one Widget changes in your layout, only the box in which it is loaded needs to be recalculated by the system.

Widget library

Flutter Widgets Framework

The Widget library is perhaps the most interesting library. It is another abstraction layer used to provide Widgets out of the box. All Widgets in this library belong to one of the following three types of Widgets that are handled with the appropriate RenderObject.

  • Layout, such as Column and Row Widgets are used to help us easily handle the layout of other Widgets.
  • Painting, such as Text and Image Widgets allow us to display (draw) some content on the screen.
  • Hit-Testing such as GestureDetector allows us to recognize different gestures such as tap and swipe.

In most cases we will use some "basic" Widgets to compose the Widgets we need. For example, we use GestureDetector to wrap the Container, and the Button is wrapped in the Container to handle button clicks. This is called composition rather than inheritance. However, in addition to building each UI component themselves, the Flutter team also created two libraries containing commonly used Material and Cupertino style Widgets.

Material & Cupertino library

Widgets library using Material and Cupertino design specifications.

In order to reduce the burden on developers, Flutter created this Widgets layer with Material and Cupertino styles.

Flutter Engine

  • Engine: The core of Flutter is the Flutter engine. Most of the engine layer is implemented in C++ and supports all primitives required by Flutter applications. This engine is responsible for rasterizing the composited scene whenever a new frame needs to be drawn. It provides the underlying implementation of Flutter's core API, including graphics (via Skia), text layout, file and network I/O, accessibility support, plug-in architecture, and Dart runtime and compilation toolchains. It is a connection framework and system (Andoird /iOS) bridge. Mainly include: Skia, Dart and Text.
    • The Flutter engine is exposed to the Flutter framework through dart:ui, which encapsulates the underlying C++ code in the Dart class. This library exposes the lowest-level primitives, such as classes for driving input, graphics, and text rendering subsystems.
    • Skia is an open source two-dimensional graphics library that provides a common API for a variety of hardware and software platforms. It has been used as the graphics engine of Google Chrome, Chrome OS, Android, Mozilla Firefox, Firefox OS and many other products. The supported platforms also include Windows, macOS, iOS, Android, Ubuntu, etc.
    • The Dart part mainly includes: Dart Runtime, Garbage Collection (GC), if it is Debug mode, it also includes JIT (Just In Time) support. In Release and Profile modes, AOT (Ahead Of Time) is compiled into native arm code, and there is no JIT part.
    • Text is text rendering, and its rendering levels are as follows: libtxt library derived from Minikin (for font selection and line separation); HartBuzz is used for glyph selection and shaping; Skia is used as a rendering/GPU backend, using FreeType rendering on Android and Fuchsia , using CoreGraphics on iOS to render fonts.

Flutter Embedder

  • Embedded layer (Embedder): The platform embedded layer is a native system application used to render all Flutter content, and it acts as the glue between the host operating system and Flutter. When starting a Flutter application, the embedding layer provides an entry point, initializes the Flutter engine, acquires UI and rasterization threads, and creates textures that Flutter can write to. The embedding layer is also responsible for managing the life cycle of the application, including input operations (such as mouse, keyboard, and touch), window size changes, thread management, and platform message delivery. Flutter has platform embedding layers for Android, iOS, Windows, macOS, and Linux.
    Flutter's interface construction, layout, composition, and drawing are all done by Flutter itself, rather than converted into native components of the corresponding platform system. The method of obtaining the underlying life cycle of the texture and linkage application will inevitably change according to the platform characteristics. The Flutter engine itself is platform-independent, providing a stable ABI (Application Binary Interface), including a platform embedding layer, through which Flutter can be set and used.

    • Flutter apps are packaged in the same way as other native apps to the underlying operating system.
    • A platform-specific embedder provides an entry point; coordinates with the underlying operating system to access services such as rendering surfaces, accessibility, and input; and manages the message event loop.
    • Embedders are written in the language appropriate to the platform: currently Java and C++ for Android, Objective-C/Objective-C++ for iOS and macOS, and C++ for Windows and Linux.
    • Using an embedder, Flutter code can be integrated into an existing application as a module, or it can be the entire content of the application. Flutter includes a number of embedders for common target platforms, but others exist as well.

    Each platform has its own set of APIs and limitations. Here are some brief notes about the platform:

    • On iOS and macOS, Flutter is loaded into the embedded layer via UIViewController and NSViewController respectively. These embedding layers will create a FlutterEngine, as the host of Dart VM and your Flutter runtime, and a FlutterViewController, associated with the corresponding FlutterEngine, passing UIKit or Cocoa input events to Flutter, and passing the frame content rendered by FlutterEngine through Metal or OpenGL for display.
    • On Android, Flutter is loaded as an Activity in the embedding layer by default. At this time, the view is controlled by a FlutterView, and based on the composition and z-ordering requirements of the Flutter content, the content of the Flutter is presented in view mode or texture mode.
    • On Windows, Flutter is hosted as a traditional Win32 app, and content is rendered through ANGLE, a library that translates OpenGL API calls into DirectX 11 equivalent calls. There is currently an attempt to use UWP apps as an embedding layer for Windows and replace ANGLE with direct calls to the GPU via DirectX 12.

As can be seen from the architecture diagram, the platform-related layer of Flutter is very low. The platform (such as iOS) only provides a canvas, and all the remaining rendering-related logic is inside Flutter. Flutter rewrites a cross-platform UI framework from beginning to end. , including UI controls, rendering logic and even development languages. The rendering engine is implemented by the cross-platform Skia graphics library, and only the interface related to graphics drawing depends on the system, which can guarantee the consistency of experience on different platforms and devices to the greatest extent. The logic processing uses the Dart language that supports AOT, and the execution efficiency is also high. Much higher than JavaScript.

RN core architecture

RN Core Architecture
React Architecture

  • JS Bundle no longer depends on JSC (Javascript Core). In other words, it can be compiled and used in any JS engine (V8, etc.).
  • Introduce the JSI standard, and realize the respective methods based on the JSI protocol, so that JS can directly refer to the C++ object, and vice versa. The interaction with the original is no longer glued with Bridge.
  • The rendering engine still relies on native pipelines. Guess maybe FB does not have so many years of web rendering engine experience like Google, so the wheel does not need to spend time reinventing

Rendering logic for cross-platform frameworks

At the rendering level, there are big differences between Flutter and other cross-platform frameworks, but the theory is basically the same. First, a platform-independent virtual tree (Virtual Dom Tree) is built, and then rendered by themselves or handed over to native rendering through different implementations.

insert image description here
insert image description here

  • Native Android is the native code that goes through skia and finally goes to the GPU to complete the rendering and drawing. The Android native system itself comes with skia;

  • Flutter, the controls in the Dart code go through skia and finally go to the GPU to complete the rendering and drawing. Here, the skia of the system is used on Andriod, while the skia packaged into the project is used on iOS;

    • The difference between Flutter is that the rendering directly uses skia and GPU to interact, and realizes platform-independent controls on the Android and iOS platforms. Simply put, most of the Widgets in Flutter are It has nothing to do with Android and iOS.
    • Essentially, the native platform provides a Surface-like drawing board, and the rest only needs to be rendered by Flutter to render the corresponding controls
    • Generally, FlutterView is used as the rendering host, which can be used internally on Android as SurfaceView, TextureView or FlutterImageView; on iOS it is UIView​​​​ Rendering via Layer​.
    • Therefore, Flutter controls can achieve consistent effects on different platforms, but mixing with native controls will also have high cost and difficulty. In terms of the ability to access native controls, Flutter provides a PlatformView mechanism to achieve Access, the implementation of PlatformView itself will easily cause problems such as memory and keyboard, so it also brings higher access costs.
  • ReactNative/Weex and other similar projects, they run in their respective JS engines, and finally use the native rendering capabilities to render by mapping to native controls;

​ReactNative/Weex​ Such cross-platform and native platforms are closely related:
  • The advantage is: if you need to use the control capabilities of the native platform, the access cost will be relatively low;
  • The disadvantages are naturally: rendering heavily depends on the capabilities of platform controls, more coupling, differences in native controls between different systems, and differences in the properties and effects of controls in different versions of the same system, combined in the post-development process. maintenance costs.

For example: the style debugged on iOS is abnormal on Android; the style that takes effect on Android is not supported on iOS; the control effect on iOS platform is displayed differently on Android, such as pull-to-refresh , Appbar, etc.;

Flutter layout and rendering

Since Flutter is a cross-platform framework, how does it provide comparable performance to native platform frameworks? How does it translate from the widget hierarchy to actual pixels drawn on the screen? Need to go through those steps?

Let's start thinking from the perspective of native Android applications. When you're writing something to draw, you need to call the Android framework's Java code. The Android system library provides components that can draw itself to the Canvas object, and then Android can use the Skia image engine written in C/C++ to call the CPU and GPU to complete the drawing on the device.
​Generally
speaking, cross-platform frameworks create a layer of abstraction on top of the underlying UI libraries of Android and iOS, which attempts to smooth out the differences between the various systems. At this time, the code of the application program is often written in an interpreted language such as JavaScript, and these codes will interact with the Java-based Android and Objective-C-based iOS systems, and finally display the UI interface. All processes add significant overhead, especially when there is heavy interaction between the UI and application logic.

Flutter reduces the overhead of the abstraction layer by bypassing the system UI component library and using its own widget content set. The Dart code used to draw Flutter's image content is compiled to machine code and rendered using Skia. Flutter also embeds its own copy of Skia, allowing developers to follow up and upgrade their applications when the device is not updated to the latest system, ensuring stability and improving performance.

From user action to GPU

insert image description here
insert image description here

Build: From Widget to Element

First observe the following code snippet, which represents a simple widget structure:

Container(
  color: Colors.blue,
  child: Row(
    children: [
      Image.network('https://www.example.com/1.png'),
      const Text('A'),
    ],
  ),
);

When Flutter needs to draw this code snippet, the framework calls the build() method, which returns a widget subtree that draws the UI based on the current application state. During this process, the build() method may introduce new widgets according to the state when necessary. In the above example, the color and child of Container are typical examples. We can view the source code of Container, you will see that when the color property is not empty, ColoredBox will be added for color layout.

if (color != null)
  current = ColoredBox(color: color!, child: current);

Correspondingly, Image and Text will also introduce RawImage and RichText during the construction process. In this way, the final generated widget structure is deeper than the level represented by the code, as shown in the following figure in this scenario

insert image description here
That's why when you use the Flutter inspector of Dart DevTools to debug the widget tree structure, you will find that the actual structure is deeper than the structure level in your original code.

During the construction phase, Flutter will convert the widgets described in the code into corresponding Element trees, and each Widget has a corresponding Element. Each Element represents a widget instance at a specific position in the tree hierarchy. There are currently two basic types of Element:

  • ComponentElement, host of other Elements.
  • RenderObjectElement, an Element that participates in the layout or drawing phase.

insert image description here
RenderObjectElement is the bridge between the underlying RenderObject and the corresponding widget, we will introduce it later.
Any widget can refer to an Element through its BuildContext, which is a handle to the widget's position in the tree. Similar to the context in the Theme.of(context) method call, it is passed as a parameter of the build() method.

Since widgets and their relationships are immutable, any operation on the widget tree (such as replacing Text('A') with Text('B')) will return a new collection of widget objects. But that doesn't mean that the underlying rendered content has to be rebuilt. The Element tree is persistent between each frame, so it plays a vital role in performance. Flutter relies on this advantage to implement a mechanism that seems to be completely abandoned and the underlying representation is cached. Flutter can rebuild the part of the Element tree that needs to be reconfigured according to the changed widget.

layout and rendering

Few applications draw only a single widget. Therefore, effectively arranging the structure of widgets and determining the size and position of each Element before rendering is completed is one of the key points of all UI frameworks.

In the render tree, the base class of each node is RenderObject, which defines an abstract model for layout and drawing. It's the most mundane thing: it's not always a fixed size, and it doesn't even follow the Cartesian coordinate laws (as shown by the polar coordinate system example). Every RenderObject knows about its parent, but has no more information about its children than how to access and obtain their layout constraints. This design allows RenderObject to have efficient abstraction capabilities and can handle a variety of usage scenarios.

During the construction phase, Flutter will create or update a corresponding object inherited from RenderObject for each RenderObjectElement in the Element tree. RenderObjects are actually primitives: RenderParagraph for rendering text, RenderImage for rendering images, and RenderTransform for applying transformations before drawing child node content are higher-level implementations.
insert image description here
Most Flutter widgets are rendered by an object that inherits a subclass of RenderBox, and the RenderObject they present will have a fixed size in a two-dimensional Cartesian space. RenderBox provides a box bounding model that associates a rendered minimum and maximum width and height for each widget.

When laying out, Flutter traverses the rendering tree in a DFS (depth-first traversal) manner, and passes constraints from parent nodes to child nodes in a top-down manner. For child nodes to determine their own size, they must obey the constraints passed by the parent node. Child nodes respond by passing sizes to the parent node in a bottom-up fashion within the constraints established by the parent node.
insert image description here
After traversing the tree once, each object has a definite size through parent constraints and can be rendered by calling paint() at any time.
The box-constrained model is very powerful, and the time complexity of its object layout is O(n):

Parent nodes can determine the size of their child node objects by setting maximum and minimum size constraints. For example, in a mobile app, the top-level renderer object would limit the size of its children to the size of the screen. (Child nodes can choose how to take up space. For example, they may be laid out centered within set constraints.) ​The parent
node
can determine the width of the child node, and let the child node flexibly adapt to the layout height (or determine the height while Adaptive Width). An example in reality is the text of the fluid layout. They often fill the horizontal limit, and then determine the height according to the amount of text content.
​Such
a box constraint model is also applicable to the scene where the child node object needs to know how much space is available to render its content. By using the LayoutBuilder widget, the child node can get the constraints passed down from the upper layer and make reasonable use of the constraint objects. Use Methods as below:

Widget build(BuildContext context) {
    
    
  return LayoutBuilder(
    builder: (context, constraints) {
    
    
      if (constraints.maxWidth < 600) {
    
    
        return const OneColumnLayout();
      } else {
    
    
        return const TwoColumnLayout();
      }
    },
  );
}

More information about the constraints and layout system, as well as examples, can be found in Understanding Flutter Layout Constraints in depth article.

The root node of all RenderObjects is RenderView, which represents the overall output of the render tree. When the platform needs to render a new frame of content (such as a vsync signal or a texture update completion), it will call the compositeFrame() method, which is part of RenderView. This method will create a SceneBuilder to trigger the update of the current screen. When the screen is updated, RenderView will pass the synthesized screen to the Window.render() method in dart:ui to control the GPU for rendering. ​More details can
about learn more in this discussion about the Flutter rendering pipeline.

Widget tree, Element tree, RenderObject tree, connection

How does Flutter create layouts? How is RenderObject connected with Widgets? What is Element? Let's look at a simple example next to understand the relationship between them.

initial build

The APP we built is very simple. It consists of three Stateless Widgets: SimpleApp, SimpleContainer, SimpleText. So what happens if we call Flutter's runApp() method? When runApp() is called, the following events will happen in the background for the first time.

  1. Flutter will build a Widgets tree containing these three Widgets.
  2. Flutter traverses the Widget tree, then calls createElement() based on the Widgets in it to create corresponding Element objects, and finally assembles these objects into an Element tree.
  3. The third tree is created, which contains the RenderObject created by the Element corresponding to the Widget through createRenderObject(). The figure below shows the state of Flutter after these three steps:

insert image description here
Flutter creates three different trees, one for Widget, one for Element, and one for RenderObject. Each Element has a reference to the corresponding Widget and RenderObject.

Widget

Everything in Flutter is a Widget. Widget is very lightweight, instantiation consumes little performance, so it is the best tool to describe the state of APP (that is, configuration)

RenderObject

So what is RenderObject? RenderObject contains all the logic used to render the instance Widget. It is responsible for layout, painting and hit-testing. Its generation is expensive, so we should cache it as much as possible. We keep it in memory for as long as possible, and even recycle them (because their instantiation is really resource-intensive), at this time Element needs to appear.

Element

Element is the bridge that exists between the mutable Widget tree and the immutable RenderObject tree. Element is good at comparing two Objects, such as Widget and RenderObject in Flutter. Its role is to configure the position of the Widget in the tree and keep references to the corresponding RenderObject and Widget.

Performance optimization - the entire Flutter APP is like a RecycleView.

Why use three trees instead of one? In short it's for performance. When the Widget tree changes, Flutter uses the Element tree to compare the new Widget tree with the original RenderObject tree. If the types of Widget and RenderObject in a certain position are inconsistent, the RenderObject needs to be recreated. If the types of Widgets and RenderObjects in other locations are the same, only the configuration of RenderObject needs to be modified, and there is no need to instantiate RenderObject which consumes performance.

Because Widget is very lightweight, instantiation consumes little performance, so it is the best tool to describe the state of APP (that is, configuration). The heavyweight RenderObject (creation is very performance-consuming) needs to be created as little as possible and reused as much as possible. As Simon said: the entire Flutter APP is like a RecycleView. However, in the framework, Elements are abstracted, so you don't need to deal with them very often. The context passed in the build (BuildContext context) method of each Widget is the Element that implements the BuildContext interface, which is why a single Widget of the same category is different.

Page update - three tree changes

Because Widgets are immutable, when a Widget's configuration changes, the entire Widget tree needs to be rebuilt. For example, when we change the color of a Container to red, the framework will trigger an action to rebuild the entire Widget tree. Then with the help of Element, Flutter compares the type of the first Widget in the new Widget tree with the type of the first RenderObject in the RenderObject tree. Then compare the type of the second Widget in the Widget tree and the second RenderObject in the RenderObject tree, and so on until the comparison between the Widget tree and the RendObject tree is completed.

Flutter follows one of the most basic principles: to determine whether the new Widget and the old Widget are of the same type. If they are not of the same type, remove Widget, Element, and RenderObject from their trees (including their subtrees) respectively, and then create new objects. If it is a type, just modify the configuration in RenderObject, and then continue to traverse down.

Do not change the type, only modify the attribute

When we do not change the type but only modify a color property, the SimpleApp Widget is the same type as the original, and its configuration is the same as the original SimpleAppRender, so nothing will happen. The next item in the Widget tree is the SimpleContainer Widget. Its type is the same as before, but its color has changed and the configuration of the RenderObject has changed. Because the SimpleObject still needs a SimpleContainerRender to render, Flutter just updates the SimpleContainerRender's color property and then asks it to re-render. All other objects remain unchanged.
insert image description here

Widget type changed

Flutter walks down the top of the Widget tree and compares it to the RenderObject type in the RenderObject tree.
insert image description here
Because the type of SimpleButton is different from the type of Element at the corresponding position in the Element tree (in fact, it is still compared with the type of RenderObject), Flutter will delete this Element and the corresponding SimpleTextRender from their respective trees. Then Flutter will rebuild the Element and RenderObject corresponding to the SimpleButton.
insert image description here
The RenderObject tree has been rebuilt and the layout will be calculated and drawn on the screen.

Flutter packaging and debugging

Before running Flutter, you need to execute ​​flutter pub get​​​to download the third-party code synchronously. The downloaded third-party code generally exists ​​/Users/你的用户名/.pub-cache​​in the (Mac) directory.

After the dependencies are successfully downloaded, you can directly start the Flutter project by clicking Run ​​flutter run​​or the IDE tool. This process will require some network synchronization work for the native project, such as:

  • Synchronize Gradle and aar dependency packages on Android;
  • On iOS, pod install is required to synchronize some dependent packages;

If you need to check the progress during project sync:

  • Android can execute ./gradlew assembleDebug in the android/​​ directory to view the synchronization progress;
  • For iOS, you can execute pod install in the ios/​​ directory to check the download progress;

Among the synchronized plug-ins, if it is a Plugin with native platform code logic, you can see a file called .flutter_plugins and .flutter-plugins-dependencies in the root directory of the project. They are git ignore files. Android and iOS will refer to the local path plugin according to this file, and later Flutter will dynamically add dependencies according to this path when it runs.

  • By default, Flutter is in the JIT mode of operation under debug, so the operation efficiency will be relatively low, and the speed is relatively slow, but it can be hotloaded.

  • In the release mode, it is in AOT mode, and the running speed will be much faster. At the same time, Flutter generally uses the CPU to run by default on the simulator, and uses the GPU to run on the real machine, so the performance is also different.

  • In addition, if the debug operation is run on the real iOS 14 device, it will not work after the connection is broken and restarted.

  • If there is a cache problem in the project, you can directly execute flutter clean to clear the cache.

Why doesn't Flutter support hot updates?
ReactNative and Weex convert the controls in the JS code into native controls for rendering, so in essence the JS code part is just text, and using code-push to push text content does not violate the platform requirements in essence.
However, the files packaged by Flutter are binary files, and pushing binary files obviously does not meet the platform requirements.

The release packaged Android will generate two dynamic libraries app.so​ and flutter.so​;
iOS will generate App.framework​ and Flutter.framework document.

Flutter mainly prioritizes understanding these three points: responsiveness, Widgets and state management.

Responsive

Responsive programming is also called declarative programming, which is the mainstream of front-end development. Of course, it is a trend for client-side development, such as​Jetpack Compose​​,​SwiftUI​​.

Jetpack Compose and Flutter are really similar on some surface levels.

Responsive simply means that you don’t need to manually update the interface, you only need to “declare” the interface through code, and then connect the relationship between the data and the interface. When the data is updated, the interface will naturally be updated.

From the perspective of code, for native development, there is no xml layout, no storyboard, the layout is completely completed by the code, what you see is what you get, and there is no need for the operation interface "object" to carry out Assignment and update, all you need to do is configure the relationship between data and interface.

Responsive development is different from data binding or MVVM in that it rebuilds and adjusts the entire rendering tree every time, rather than simply performing visibility operations on the UI.

Widget

Widget is the basic concept in Flutter, and it is also the most direct object for us to write code. Everything in Flutter is a Widget. Widgets are immutable, and each Widget state represents a frame.

So Widget as an immutable object, it cannot be a real working UI object, the real View level objects in Flutter are Element and RenderObject , where the abstract object of Element is the ​BuildContext​ that we often use.

Widget in Flutter is more of a configuration file, which is used to describe the configuration code of the interface.

state management

As a responsive development framework, Flutter essentially no longer pursues the design patterns of MVC, MVP, and MVVVM. It is more about the management of the interface state.

It is to abandon the idea that you need to get the View object on the native platform, and then set the UI for it.

More data flow needs to be managed on Flutter, such as:

  • Where is the data sent from and where is it consumed;
  • Whether the data is unidirectional or bidirectional;
  • What intermediate transformations the data needs to go through;
  • From which layer the data is passed down;
  • Where the data is bound;
  • How to achieve partial refresh in multiple places;

Because for the interface, it only needs to change according to the data, and we don’t need to obtain it to set it up separately, so there are various data management and sharing frameworks in Flutter, and the more popular ones are provider​​​​,​ ​getx​, ​flutter_redex​, ​flutter_mobx​, etc.

Guess you like

Origin blog.csdn.net/rd_w_csdn/article/details/125873980