Flutter Architecture
To put it simply, Flutter can be divided into three layers from top to bottom: framework layer , engine layer and embedding layer . We will introduce them separately below:
1. Frame layer
Flutter Framework , the framework layer. This is a pure Dart-implemented SDK, which implements a set of basic libraries, from the bottom up, let's briefly introduce:
-
The bottom two layers (Foundation and Animation, Painting, Gestures) are merged into a dart UI layer in some Google videos, corresponding to the dart:ui package in Flutter, which is the underlying UI library exposed by Flutter Engine, providing animation, Gestures and drawing capabilities.
-
The Rendering layer, that is, the rendering layer, is an abstract layout layer that depends on the Dart UI layer. The rendering layer will build a rendering tree composed of renderable objects. When these objects are dynamically updated, the rendering tree will Figure out what changed, and update the render. The rendering layer can be said to be the core part of the Flutter framework layer. In addition to determining the position and size of each rendering object, it also performs coordinate transformation and drawing (calling the underlying dart:ui).
-
The Widgets layer is a set of basic component libraries provided by Flutter. On top of the basic component library, Flutter also provides two visual style component libraries, Material and Cupertino, which respectively implement the Material and iOS design specifications.
The Flutter framework is relatively small, because some higher-level functions that developers may use have been split into different packages, implemented using Dart and Flutter's core libraries, including platform plugins such as camera and webview , and platform-independent features such as animations .
2. Engine layer
Engine , the engine layer. There is no doubt that it is the core of Flutter. This layer is mainly implemented in C++, including Skia engine, Dart runtime (Dart runtime), text layout engine, etc. When the code calls the dart:ui library, the call will eventually go to the engine layer, and then the real drawing and display will be realized.
3. Embedding layer
Embedder , the embedding layer. The final rendering and interaction of Flutter depends on the operating system API of its platform, and the embedding layer is mainly to "install" the Flutter engine on a specific platform. The embedding layer is written in the language of the current platform, such as Java and C++ for Android, Objective-C and Objective-C++ for iOS and macOS, and C++ for Windows and Linux. Flutter code can be integrated into existing applications in a modular way through the embedding layer, or it can be used as the main body of the application. Flutter itself contains the embedding layer of each common platform. If Flutter wants to support a new platform in the future, you need to write an embedding layer for the new platform.
Generally speaking, developers do not need to be aware of the existence of Engine and Embedder (if they do not need to call platform system services), Framework is what developers need to interact with directly, so it is also at the top of the entire layered architecture model.
Widget interface
In Flutter, the function is to widget
" describe the configuration information of a UI element ". That is to say, it does not actually represent the display elements that are finally drawn Widget
on the device screen . The content, alignment, and text style of are all its configuration information. Let's take a look at the class declaration:Widget
Text
Widget
// 不可变的
abstract class Widget extends DiagnosticableTree {
const Widget({
this.key });
final Key? key;
@factory
Element createElement();
String toStringShort() {
final String type = objectRuntimeType(this, 'Widget');
return key == null ? type : '$type-$key';
}
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.defaultDiagnosticsTreeStyle = DiagnosticsTreeStyle.dense;
}
bool operator ==(Object other) => super == other;
int get hashCode => super.hashCode;
static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType
&& oldWidget.key == newWidget.key;
}
...
}
@immutable
It means that the Widget is immutable , which restrictsWidget
the properties defined in (that is, configuration information) to be immutable (final
), whyWidget
are the properties defined in not allowed to change? This is because in Flutter, if the attribute changes, the tree will be rebuiltWidget
, that is, a newWidget
instance will be recreated to replace the oldWidget
instance , soWidget
it is meaningless to allow the attribute change, because onceWidget
its own attribute changes, it will be replaced . This is whyWidget
properties defined in must befinal
.Widget
The class inherits from ,DiagnosticableTree
the "diagnostic tree" , whose main role is to provide debugging information.DiagnosticableTree
Key
: Thiskey
attribute is similar toReact/Vue
the one inkey
, the main function is to decide whether to reuse the old one next timebuild
widget
, the decision condition is incanUpdate()
the method.createElement()
: As mentioned above, "onewidget
can correspond to multipleElement
"; when the Flutter framework builds the UI tree, it will first call this method to generate objects corresponding to the nodesElement
. This method is implicitly called by the Flutter framework, and it is basically not called during our development.debugFillProperties(...)
The method of overriding the parent class is mainly to set some characteristics of the diagnostic tree.canUpdate(...)
It is a static method, which is mainly used to reuse the old widget when the widget tree is rebuilt . In fact, it should be: whether to use the new widget object to update the configuration of the corresponding Element object on the old UI tree ; through We can see its source code, as long as thenewWidget
sum of and is equal , the new widget will be used to update the configuration of the object, otherwise a new one will be created .oldWidget
runtimeType
key
Element
Element
In Flutter, it can be said that everything is everything Widget
. Even a centering ability, in traditional imperative UI development, is usually set as a property, but in Flutter it is abstracted into a named Center
component. In addition, Flutter advocates radical combination development, that is, to Widget
construct your goals through a series of basic structures as much as possible Widget
.
Based on the above two characteristics, Flutter's code will be full of various Widget
, and every frame of UI update means partial Widget
reconstruction. You may worry whether this design is too bloated and inefficient. In fact, on the contrary, this is the cornerstone of Flutter's ability to perform high-performance rendering. The reason why Flutter is designed in this way is also intentional based on the following two facts:
-
Widget
The more nodes on the Widget Tree , the more accurate and smaller the part that needs to be reconstructed through the DiffWidget
algorithm, and the main performance bottleneck of UI rendering is the reconstruction of nodes. -
The object model and GC model of the Dart language optimize the fast read allocation and recovery of small objects, and it is
Widget
this small object .
Three Trees in Flutter
Since Widget only describes the configuration information of a UI element, who does the actual layout and drawing?
The processing flow of the Flutter framework is as follows:
Widget
Generate a tree based on the treeElement
, andElement
the nodes in the tree inherit from theElement
class.- The tree (render tree) is generated according to
Element
the treeRender
, and the nodes in the render tree inherit fromRenderObject
the class. - Generate a tree based on the rendering tree
Layer
, and then display it on the screen.Layer
The nodes in the tree all inherit fromLayer
the class.
The real layout and rendering logic is in Render
the tree, Element
which is the glue of Widget
and , which can be understood as an intermediate agent.RenderObject
The respective functions of the three trees are:
Widget
: Responsible for configuration. In order toElement
describe the configuration information of the UI, it has a public API. This part is also a part that developers can directly perceive and use.Element
: The manager of Flutter Virtual DOM,widget
the life cycle it manages, represents the UI data that actually exists in memory, it is responsible for thewidget
instantiation of specific locations in the tree,widget
the references it holds, and the management of parent-child relationships in the tree, in fact,Widget Tree
withRenderObject Tree
areElement Tree
generated by the driver.RenderObject
: Responsible for handling size, layout and drawing, it will draw itself, place child nodes, etc.
Note here:
- Among the three trees,
Element
andWidget
are in one-to-one correspondence, butElement
andRenderObject
are not in one-to-one correspondence. For exampleStatelessWidget
andStatefulWidget
have no correspondingRenderObject
. - The rendering tree will generate a tree before it is displayed on the screen
Layer
, so there are actually four trees in Flutter, but we only need to understand the above three trees.
StatelessWidget
StatelessWidget
Relatively simple, it inherits from a widget
class and overrides createElement()
the method:
createElement() => StatelessElement(this);
StatelessElement
StatelessElement
Indirectly inherited from the Element
class, StatelessWidget
corresponding to (as its configuration data).
StatelessWidget
Used in scenarios that do not need to maintain state , it usually builds the UI build
by nesting other in the method widget
, and recursively builds its nested ones during the construction process widget
.
Here is a simple example:
class Echo extends StatelessWidget {
const Echo({
Key? key,
required this.text,
this.backgroundColor = Colors.grey, //默认为灰色
}):super(key:key);
final String text;
final Color backgroundColor;
Widget build(BuildContext context) {
return Center(
child: Container(
color: backgroundColor,
child: Text(text),
),
);
}
}
We can then use it as follows:
Widget build(BuildContext context) {
return Echo(text: "hello world");
}
By convention,
widget
the constructor parameters should use named parameters , and the parameters that must be passed in the named parameters should be added withrequired
keywords, which is beneficial for static code analyzers to check; when inheritingwidget
, the first parameter should usually beKey
. Also, ifwidget
receivers are requiredwidget
, thechild
orchildren
parameter should usually be placed last in the parameter list. Also by convention,widget
properties should be declared as much as possible tofinal
prevent accidental changes.
Context
build
The method has a context
parameter, which is BuildContext
an instance of the class, representing the current contextwidget
in widget
the tree , and each corresponds to a object.widget
context
In fact, it is a handle ( ) to perform "related operations" in the context
current position widget
in the tree , for example, it provides methods for traversing the tree upwards from the current and finding the parent by type .widget
handle
widget
widget
widget
widget
widget
Here's an example of getting the parent in a subtree :
class ContextRoute extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Context测试"),
),
body: Container(
child: Builder(builder: (context) {
// 在 widget 树中向上查找最近的父级`Scaffold` widget
Scaffold scaffold = context.findAncestorWidgetOfExactType<Scaffold>();
// 直接返回 AppBar的title, 此处实际上是Text("Context测试")
return (scaffold.appBar as AppBar).title;
}),
),
);
}
}
StatefulWidget
If you have one StatelessWidget
, why have another StatefulWidget
? Widget
All are immutable in Flutter , but actually need to be refreshed according to the corresponding state Widget
, so it is generated StatefulWdiget
, which is composed StatefulWidget
of two objects Widget
and .State
StatefulWidget
It also inherits from widget
the class and rewrites createElement()
the method. The difference is that the returned Element
objects are not the same, but one is returned StatefulElement
; in addition, StatefulWidget
a new interface is added to the class createState()
.
abstract class StatefulWidget extends Widget {
const StatefulWidget({
Key key }) : super(key: key);
StatefulElement createElement() => StatefulElement(this);
State createState();
}
-
StatefulElement
Indirectly inherited from theElement
class,StatefulWidget
corresponding to (as its configuration data).StatefulElement
may be called multiple timescreateState()
to create the state(State
) object. Developers don't need to care about this method. -
createState()
For creating andStatefulWidget
related states, developers must implement this method in subclasses. ItStatefulWidget
may be called multiple times during the lifetime of the . For example, when a treeStatefulWidget
is inserted intowidget
multiple positions at the same time, the Flutter framework will call this method to generate an independentState
instance for each position. In fact, it is essentially oneStatefulElement
corresponding to oneState
instance.
In
StatefulWidget
,State
objectsStatefulElement
have a one-to-one correspondence with each other, so in Flutter's SDK documentation, you can often see descriptions such as "remove a State object from the tree" or "insert a State object into the tree". The tree refers to the Element tree generated by the widget tree. The "tree" is often mentioned in Flutter's SDK documentation, and we can judge which tree it refers to based on the context. In fact, for users, we don’t need to care about which tree it is. No matter what kind of tree its ultimate goal is to describe the structure and drawing information of the UI, we only need to understand it as “a node tree that constitutes the user interface” , don't get too entangled in these concepts.
State
A StatefulWidget
class corresponds to a State
class, indicating the state to be maintained State
corresponding to it , and the saved state information in it can be:StatefulWidget
State
State
Can be read synchronously atwidget
build time .State
It can be changed duringwidget
the life cycle , and the method can be manually called to change, which will notify the Flutter framework of the state change. After receiving the message, the Flutter framework will call its method again to rebuild the tree, so as to achieve the purpose of updating the UI.setState()
State
build
widget
State
There are two common properties in :
-
State.widget
State
, through which we can get the instance associated with this instancewidget
, which is dynamically set by the Flutter framework. Note that this association is not permanent, because in the application life cycle, the instance of a node on the UI treewidget
may change when it is rebuilt, butState
the instance will only be created when it is inserted into the tree for the first time. When rebuilding, ifwidget
is modified, the Flutter framework will dynamically set toState.widget
point to the newwidget
instance . -
State.context
, it isStatefulWidget
correspondingBuildContext
and has the sameStatelessWidget
effectBuildContext
. When the framework needs to create oneStatefulWidget
, it callsStatefulWidget.createState()
the method and then associatesState
the object with the objectBuildContext
. This association is permanent,State
the object never changes itBuildContext
, butBuildContext
itself can be moved around the tree.
State life cycle
After State
the object is createState()
created by the method, it will call initState()
the method and start its own life cycle:
The meaning of each callback function:
-
initState()
: It will be called whenwidget
it is inserted into the tree for the first timewidget
. For eachState
object, the Flutter framework will only call this callback once . Therefore, you usually do some one-time operations in this callback, such as state initialization and subtree event subscription notification etc.Note: It cannot
initState
be called inBuildContext.dependOnInheritedWidgetOfExactType
(this method is used towidget
getwidget
the closest parent on the treeInheritedWidget
), because after the initialization is completed,widget
the treeInheritFrom widget
may also change, so the correct way should be inbuild()
the method ordidChangeDependencies()
in call it. -
didChangeDependencies()
:State
It will be called when the dependency of the object changes; for example, ifbuild()
one is included in the previousInheritedWidget
and then changedbuild()
in the later, then the callbacks of the sub will be called at this time. A typical scenario is that when the system language or application theme changes, the Flutter framework will notify to call this callback. It should be noted that when the component is first created and mounted (including re-created), the corresponding one will also be called.Inherited widget
InheritedWidget
widget
didChangeDependencies()
Locale
widget
didChangeDependencies
-
build()
: Mainly used to buildwidget
subtrees, it will be called in the following scenarios:- after the call
initState()
. - after the call
didUpdateWidget()
. - after the call
setState()
. - after the call
didChangeDependencies()
. - Called after an object has been removed
State
from one location in the tree and reinserted elsewhere in the tree .deactivate
- after the call
-
reassemble()
: A callback specially provided for development and debugging. It will be called during hot reload . This callback will never be called in Release mode. -
didUpdateWidget()
: Whenwidget
rebuilding, the Flutter framework will callwidget.canUpdate
to detectwidget
the old and new nodes at the same position in the tree, and then decide whether an update is required, and ifwidget.canUpdate
it returnstrue
, this callback will be called.As mentioned before, it will return when
widget.canUpdate
the old and new are equal at the same time , which means that it will be called in this case . (In fact, the flutter framework here will create a new Widget, bind this State, and pass the old Widget in this function) When the upper-level node rebuilds the widget, that is, when the state of the upper-level component changes, it will also trigger the execution of the sub-widget . It should be noted that if the change of the controller is involved, the listener of the old controller needs to be removed in this function, and the listener of the new controller needs to be created.widget
key
runtimeType
true
didUpdateWidget()
didUpdateWidget
-
deactivate()
:State
This callback is called when the object is removed from the tree. In some scenarios, the Flutter framework willState
reinsert the object into the tree, such asState
when the subtree containing the object moves from one position of the tree to another (this can beGlobalKey
achieved by ). The method will be called next if it was removed and not reinserted into the treedispose()
. -
dispose()
:State
Called when the object is permanently removed from the tree; resources are usually released, subscriptions are unsubscribed, animations are canceled, etc. in this callback.
Illustrate with the following counter example:
class CounterWidget extends StatefulWidget {
const CounterWidget({
Key? key, this.initValue = 0});
final int initValue;
_CounterWidgetState createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State<CounterWidget> {
int _counter = 0;
void initState() {
super.initState();
//初始化状态
_counter = widget.initValue;
print("initState");
}
Widget build(BuildContext context) {
print("build");
return Scaffold(
body: Center(
child: TextButton(
child: Text('$_counter'),
//点击后计数器自增
onPressed: () => setState(
() => ++_counter,
),
),
),
);
}
void didUpdateWidget(CounterWidget oldWidget) {
super.didUpdateWidget(oldWidget);
print("didUpdateWidget ");
}
void deactivate() {
super.deactivate();
print("deactivate");
}
void dispose() {
super.dispose();
print("dispose");
}
void reassemble() {
super.reassemble();
print("reassemble");
}
void didChangeDependencies() {
super.didChangeDependencies();
print("didChangeDependencies");
}
}
Note: When inheriting
StatefulWidget
and rewriting its method, for@mustCallSuper
the parent class method that contains annotations, the parent class method must be called in the subclass method.
Next create a new route page where we only display one CounterWidget
:
class StateLifecycleTest extends StatelessWidget {
const StateLifecycleTest({
Key? key}) : super(key: key);
Widget build(BuildContext context) {
return CounterWidget();
}
}
We run the application and open the routing page. After the new routing page is opened, a number 0 will appear in the center of the screen, and then the console log output:
I/flutter ( 5436): initState
I/flutter ( 5436): didChangeDependencies
I/flutter ( 5436): build
As you can see, the first method is called when StatefulWidget
inserting into the tree.widget
initState
Then we click the ⚡️ button to hot reload, and the console output log is as follows:
I/flutter ( 5436): reassemble
I/flutter ( 5436): didUpdateWidget
I/flutter ( 5436): build
You can see that build
before the method, only reassemble
and didUpdateWidget
are called.
Next, we widget
remove in the tree CounterWidget
, changing the method StateLifecycleTest
of to build
:
Widget build(BuildContext context) {
//移除计数器
//return CounterWidget ();
//随便返回一个Text()
return Text("xxx");
}
Then hot reload, the log is as follows:
I/flutter ( 5436): reassemble
I/flutter ( 5436): deactive
I/flutter ( 5436): dispose
As you can see, when removing CounterWidget
from widget
the tree, deactive
and dispose
are called in turn.
setState()
setState()
The method is the only legal way to modify the state in Flutter. This method will notify the framework that the internal state of this object has changed. The calling method is: it will provide setState(() {_myState = newValue;});
a callback method in which the developer must modify the state value, and the callback will immediately is called synchronously . Note that you must not perform time-consuming calculations in this callback, let alone return one Future
(the callback cannot be async
marked with " "), this callback is usually only used to wrap the actual change to the state.
After calling setState()
, the current widget
corresponding element
object will be marked as dirty
, and then build()
the method will be called to refresh the UI. The following is setState()
the life cycle of State after joining:
It seems that the State life cycle can actually be divided into three stages:
- Initialization: constructor,
initState()
,didChangeDependencies()
- State change: Execution triggered by configuration change
didUpdateWidget()
,build()
Execution causedsetState()
bybuild()
- Component removal:
deactivate()
,dispose()
Why does StatefulWidget separate State and Widget?
StatefulWidget
Why put build
the method State
in the class and not in the StatefulWidget
?
- This is mainly for development flexibility and performance considerations.
If you build()
put the method StatefulWidget
in, there are two problems in terms of flexibility:
1. Inconvenient state access.
Just imagine, if we StatefulWidget
have a lot of states, and build
the method is called every time the state changes, since the state is stored in State
, if build
the method is StatefulWidget
in, then build
the methods and 状态
are in two classes respectively, then reading the state during construction will be It will be very inconvenient! Just imagine, if build
the method is really placed StatefulWidget
in , since the process of building the user interface requires dependencies State
, build
the method will have to add a State
parameter, which is probably as follows:
Widget build(BuildContext context, State state){
//state.counter
...
}
In this case, State
all the states of the class can only be declared as public states, so that State
the states can be accessed outside the class! However, after the state is set to public, the state will no longer be private, which will cause the modification of the state to become uncontrollable. But if you build()
put the method State
in , not only can the build process directly access the state, but it also doesn't need to expose private state, which is very convenient.
2. It is inconvenient to inherit StatefulWidget.
For example, in Flutter there is a base class for animated widgets AnimatedWidget
that inherits from StatefulWidget
classes. AnimatedWidget
An abstract method is introduced in build(BuildContext context)
, and AnimatedWidget
animations inherited from widget
must implement this build
method. Now imagine that if StatefulWidget
there is already a method in the class build
, as mentioned above, build
the method needs to receive an State
object at this time, which means that AnimatedWidget
its own State
object (denoted as _animatedWidgetState
) must be provided to its subclasses, because subclasses need to be in In its build
method, the method of calling the parent class build
may be as follows:
class MyAnimationWidget extends AnimatedWidget {
Widget build(BuildContext context, State state){
// 由于子类要用到AnimatedWidget的状态对象_animatedWidgetState,
// 所以AnimatedWidget必须通过某种方式将其状态对象_animatedWidgetState 暴露给其子类
super.build(context, _animatedWidgetState)
}
}
This is obviously unreasonable, because AnimatedWidget
the state object is AnimatedWidget
an internal implementation detail and should not be exposed to the outside.
If you want to expose the state of the parent class to the subclass, you must have a transfer mechanism, and it is meaningless to do this set of transfer mechanism, because the transfer of state between the parent and child classes has nothing to do with the logic of the subclass itself.
To sum up, it can be found that, for StatefulWidget
, build
putting the method State
in can bring a lot of flexibility to the development.
On the other hand, it is mainly performance considerations. State manages the state (which can be understood as Controller), and Widget is UI (ie View). Generating Widget (ie View) according to the state change can save memory without having to create the state object State every time. .
Get the State object in the widget tree
Since the specific logic of StatefulWidget is in its State, many times, we need to obtain the State object corresponding to StatefulWidget to call some methods. For example, the state class ScaffoldState corresponding to the Scaffold component defines opening SnackBar (prompt bar at the bottom of the routing page) Methods. We have two ways to get the State object of the parent StatefulWidget in the child widget tree.
1. Get State through Context
context
Objects have a findAncestorStateOfType()
method that walks widget
up the tree from the current node to find a StatefulWidget
corresponding State
object of the specified type. Here's SnackBar
an example that implements an open :
class GetStateObjectRoute extends StatefulWidget {
const GetStateObjectRoute({
Key? key}) : super(key: key);
State<GetStateObjectRoute> createState() => _GetStateObjectRouteState();
}
class _GetStateObjectRouteState extends State<GetStateObjectRoute> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("子树中获取State对象"),
),
body: Center(
child: Column(
children: [
Builder(builder: (context) {
return ElevatedButton(
onPressed: () {
// 查找父级最近的Scaffold对应的ScaffoldState对象
ScaffoldState _state = context.findAncestorStateOfType<ScaffoldState>()!;
// 打开抽屉菜单
_state.openDrawer();
},
child: Text('打开抽屉菜单1'),
);
}),
],
),
),
drawer: Drawer(),
);
}
}
Generally speaking, if the state of StatefulWidget is private (should not be exposed to the outside), then we should not directly obtain its State object in the code; if the state of StatefulWidget is expected to be exposed (usually there are some component operations method), we can directly obtain its State object. However, the method of obtaining the state of StatefulWidget through context.findAncestorStateOfType is universal. We cannot specify whether the state of StatefulWidget is private at the grammatical level, so there is a default agreement in Flutter development: if the state of StatefulWidget is to be exposed , you should provide an of static method in StatefulWidget to obtain its State object, and developers can obtain it directly through this method; if the State does not want to be exposed, then do not provide the of method. This convention can be seen everywhere in the Flutter SDK. Therefore, the Scaffold in the above example also provides an of method, which we can actually call directly:
Builder(builder: (context) {
return ElevatedButton(
onPressed: () {
// 直接通过of静态方法来获取ScaffoldState
ScaffoldState _state=Scaffold.of(context);
// 打开抽屉菜单
_state.openDrawer();
},
child: Text('打开抽屉菜单2'),
);
}),
For another example, if we want to display snackbar, we can call it through the following code:
Builder(builder: (context) {
return ElevatedButton(
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("我是SnackBar")),
);
},
child: Text('显示SnackBar'),
);
}),
2. Get State through GlobalKey
Flutter also has a general way to get the State object - GlobalKey
get it by! The procedure is divided into two steps:
StatefulWidget
Add to targetGlobalKey
.
//定义一个globalKey, 由于GlobalKey要保持全局唯一性,我们使用静态变量存储
static GlobalKey<ScaffoldState> _globalKey= GlobalKey();
...
Scaffold(
key: _globalKey , //设置key
...
)
GlobalKey
GetState
the object by
_globalKey.currentState.openDrawer()
GlobalKey
It is a mechanism provided by Flutter to reference in the entire App element
. If a widget
is set GlobalKey
, then we can obtain the corresponding object by globalKey.currentWidget
obtaining the widget
object , and if the current is , we can obtain the corresponding object by obtaining the object .globalKey.currentElement
widget
element
widget
StatefulWidget
globalKey.currentState
widget
state
Note: Using
GlobalKey
is expensive, and should be avoided if there are other options available. In addition, the same must be uniqueGlobalKey
in the entire tree and cannot be repeated.widget
State life cycle from the perspective of navigation routing
Analysis of StatefulWidget life cycle from the perspective of StatefulElement
It can be seen that the triggering of each life cycle callback is BuildOwner
indirectly StatefulElement
driven . State
The following will analyze the trigger path and meaning of each callback from the perspective of source code.
// 代码清单8-1 flutter/packages/flutter/lib/src/widgets/framework.dart
StatefulElement(StatefulWidget widget)
: state = widget.createState(), // 触发State创建
super(widget) {
state._element = this; // State持有Element和Widget的引用
state._widget = widget;
assert(state._debugLifecycleState == _StateLifecycle.created);
}
StatefulElement
The creation is completed in its own constructorState
, and the assignment of two key fields is completed. After that, the process of this Element
node will be carried out Build
, and the specific logic is shown in the code list 8-2.
// 代码清单8-2 flutter/packages/flutter/lib/src/widgets/framework.dart
void _firstBuild() {
try {
// 触发initState回调
final dynamic debugCheckForReturnedFuture = state.initState() as dynamic;
} finally {
...... }
state.didChangeDependencies(); // 触发didChangeDependencies回调
super._firstBuild();
}
Because the above logic triggers initState
the sum didChangeDependencies
method in the life cycle, it is initState
generally used as State
the time point when internal member variables start to initialize.
didChangeDependencies
Although it will be triggered unconditionally during the first build, it will still be triggered in the subsequent Build process, as shown in Listing 8-3.
// 代码清单8-3 flutter/packages/flutter/lib/src/widgets/framework.dart
// StatefulElement
void performRebuild() {
if (_didChangeDependencies) {
// 通常在代码清单8-13中设置为true,详见8.2节
state.didChangeDependencies(); // 当该字段为true时再次触发didChangeDependencies
_didChangeDependencies = false;
}
super.performRebuild();
}
didChangeDependencies
It marks that the Element
dependent node has changed, and didChangeDependencies
the method will be called again at this time, so this callback is more suitable for responding to some dependent updates. performnRebuild
The method that will eventually be triggered StatefulElement
is build
shown in Listing 8-4.
// 代码清单8-4 flutter/packages/flutter/lib/src/widgets/framework.dart
Widget build() => state.build(this);
The above logic is in line with StatefulWidget
the intuition of use, that is, build
the method is placed State
in the callback, and the callback is mainly used for UI update. It should be noted that if the current Element
node is marked as dirty
, build
the method will definitely be called, so it is not advisable to perform time-consuming operations, so as not to affect the fluency of the UI.
For the case of non-first Build, the method that is usually triggered Element
, the logic is shown in Listing 8-5.update
StatefulElement
// 代码清单8-5 flutter/packages/flutter/lib/src/widgets/framework.dart
void update(StatefulWidget newWidget) {
// StatefulElement,见代码清单5-47
super.update(newWidget);
assert(widget == newWidget);
final StatefulWidget oldWidget = state._widget!;
_dirty = true;
state._widget = widget as StatefulWidget;
try {
_debugSetAllowIgnoredCallsToMarkNeedsBuild(true);
final dynamic debugCheckForReturnedFuture = state.didUpdateWidget(oldWidget)
as dynamic;
} finally {
_debugSetAllowIgnoredCallsToMarkNeedsBuild(false);
}
rebuild(); // 触发代码清单8-4中的逻辑
}
The above logic will trigger its method before passing the rebuild
triggered method. It is an appropriate time to remove some references and dependencies to , and update some resources that depend on properties.State
build
didUpdateWidget
oldWidget
Widget
Element
detach
The node will call _deactivateRecursively
the method in the stage, and its specific logic is shown in the code list 8-6.
// 代码清单8-6 flutter/packages/flutter/lib/src/widgets/framework.dart
static void _deactivateRecursively(Element element) {
// 见代码清单5-50
element.deactivate();
assert(element._lifecycleState == _ElementLifecycle.inactive);
element.visitChildren(_deactivateRecursively);
}
void deactivate() {
// StatefulElement
state.deactivate(); // 触发deactivate回调
super.deactivate(); // 见代码清单8-7
}
The above logic will trigger the State
method deactivate
and cause the current node to enter inactive
the stage. The callback trigger indicates that the current Element
node is removed Element Tree
, but it is still available to be re-joined. This time is suitable for releasing some resources that are strongly related to the current state. For those resources that are not related to the state, considering that the node may still Element
enter Element Tree
, it is not necessary It is suitable to be released at this time (it can be compared to the callback in Android Activity
) onPause
.
super
The method in the above logic deactivate
must be called, and its logic is shown in Listing 8-7.
// 代码清单8-7 flutter/packages/flutter/lib/src/widgets/framework.dart
void deactivate() {
// Element
if (_dependencies != null && _dependencies!.isNotEmpty) {
// 依赖清理
for (final InheritedElement dependency in _dependencies!)
dependency._dependents.remove(this);
}
_inheritedWidgets = null;
_lifecycleState = _ElementLifecycle.inactive; // 更新状态
}
The above logic is mainly used to remove the corresponding dependencies.
After the Build process ends, Element
the unmount
method will be called, and its logic is shown in Listing 8-8.
// 代码清单8-8 flutter/packages/flutter/lib/src/widgets/framework.dart
// StatefulElement,见代码清单5-52
void unmount() {
super.unmount();
state.dispose(); // 触发dispose回调
state._element = null;
}
void unmount() {
// Element
final Key? key = _widget.key;
if (key is GlobalKey) {
key._unregister(this); // 取消注册
}
_lifecycleState = _ElementLifecycle.defunct;
}
In the above logic, StatefulElement
the main method will be State
called dispose
. Element
The general logic in is responsible for destroying GlobalKey
the associated registration.
App life cycle from the perspective of Flutter
It should be pointed out that if you want to know the life cycle of the App, you need to obtain it WidgetsBindingObserver
through didChangeAppLifecycleState
. The life cycle state that can be obtained through this interface is in AppLifecycleState
the class. Common states include the following:
- resumed : The interface is visible and can respond to user input, the same as Android's
onResume
- inactive : When the interface retreats to the background or pops up a dialog box, it is in an inactive state, that is, it loses focus and does not receive user input, but the
drawFrame
callback can still be executed; the same as AndroidonPause
- paused : The application is suspended and currently invisible to the user, such as retreating to the background, losing focus, unable to respond to user input, and will not receive
drawFrame
callbacks (the engine will not call backPlatformDispatcher
) ; sameonBeginFrame
asonDrawFrame
AndroidonStop
- detached : The app is still hosted on the flutter engine, but has been detached from any host views and callbacks will no longer be executed
drawFrame
; when the app is in this state, the engine runs without a view. This could be in the process of attaching the view when the engine is first initialized, or afterNavigator
the view is destroyed due to popping.
Example usage:
class _MyHomePageState extends State<MyHomePage> with WidgetsBindingObserver {
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
void dispose() {
super.dispose();
WidgetsBinding.instance.removeObserver(this);
}
void didChangeAppLifecycleState(AppLifecycleState state) {
print(state);
}
}
How Flutter compares to other cross-platform solutions
Today, there are already many cross-platform frameworks in the industry (specifically referring to the Android and iOS platforms). According to their principles, they are mainly divided into three categories:
- H5 + native (Cordova, Ionic, WeChat applet)
- JavaScript development + native rendering (React Native, Weex)
- Self-drawn UI + native (Qt for mobile, Flutter)
The development language in the above table mainly refers to the development language of the application layer, and the development efficiency refers to the efficiency of the entire development cycle, including coding time, debugging time, and time for debugging and handling compatibility issues. Dynamic mainly refers to whether it supports dynamic code delivery and whether it supports hot update. It is worth noting that the Release package of Flutter is compiled using the Dart AOT mode by default, so it does not support dynamization, but Dart also has JIT or snapshot operation modes, and these modes support dynamization.
Solution 1: H5 + native
The main principle of this type of framework is to implement the content that needs to change dynamically in the App through HTML5 (H5 for short), and load it through the native web page loading control WebView (Android) or WKWebView (iOS). In this solution, the H5 part can be changed at any time without publishing a version, and the dynamic requirements can be met; at the same time, since the H5 code only needs to be developed once, it can run on both Android and iOS platforms at the same time, which can also reduce development time. Cost, that is, the more H5 part functions, the smaller the development cost. We call this H5 + native development mode hybrid development . Apps developed in hybrid mode are called hybrid applications or HTMLybrid Apps . If most of the functions of an application are implemented by H5, we call it Web App .
Typical representatives of the current hybrid development framework are: Cordova, Ionic. Most apps will have some functions developed by H5, at least so far, HTMLybrid App is still the most versatile and mature cross-end solution.
Here, we need to mention the small program. At present, the development technology stack of the small program application layer of various domestic companies is the Web technology stack, and the underlying rendering method is basically a combination of WebView and native.
Hybrid development technology point
1) Example: JavaScript calls the native API to get the phone model
Next, we take Android as an example to implement a native API for obtaining the phone model for calling JavaScript
. In this example, JavaScript
the process of calling native API will be shown. We choose the dsBridge of the open source library on Github as JsBridge
the communication.
- First implement the API for obtaining the phone model in the native
class JSAPI {
public Object getPhoneModel(Object msg) {
return Build.MODEL;
}
}
WebView
Register the native API throughJsBridge
the
import wendu.dsbridge.DWebView
...
//DWebView继承自WebView,由dsBridge提供
DWebView dwebView = (DWebView) findViewById(R.id.dwebview);
//注册原生API到JsBridge
dwebView.addJavascriptObject(new JsAPI(), null);
JavaScript
Call native API in
var dsBridge = require("dsbridge")
//直接调用原生API `getPhoneModel`
var model = dsBridge.call("getPhoneModel");
//打印机型
console.log(model);
The above example demonstrates JavaScript
the process of calling native APIs. Similarly, generally speaking, the excellent ones JsBridge
also support native calls JavaScript
, dsBridge
and they are also supported. If you are interested, you can go to the Github dsBridge project home page to view.
Now, let's look back, a hybrid application is nothing more than pre-implementing a series of APIs for JavaScript
calling in the first step, so that JavaScript
you have the ability to access system functions. Seeing this, I believe you can also implement a hybrid development framework yourself.
summary
The advantages of hybrid applications are: dynamic content can be developed with H5, and H5 is a Web technology stack. The Web technology stack has an open ecology and rich community resources, and the overall development efficiency is high. The disadvantage is that the performance experience is not good, and for complex user interfaces or animations, WebView can sometimes be overwhelmed.
Solution 2: JavaScript development + native rendering
React Reactive Programming
React is a responsive web framework. Let's first understand two important concepts: DOM tree and responsive programming.
DOM tree
The Document Object Model (DOM for short) is a standard programming interface recommended by the W3C organization for processing Extensible Markup Language, a platform- and language-independent way to access and modify the content and structure of a document. In other words, this is a standard interface for representing and manipulating an HTML or XML document. In simple terms, DOM is the document tree, which corresponds to the user interface control tree. In front-end development, it usually refers to the rendering tree corresponding to HTML, but in a broad sense, DOM can also refer to the control tree corresponding to the XML layout file in Android. The term DOM operation It refers to directly operating the rendering tree (or control tree). Therefore, it can be seen that the DOM tree and the control tree are equivalent concepts, but the former is often used in Web development, while the latter is often used in native development.
reactive programming
An important idea is put forward in React: the UI changes automatically when the state changes. The React framework itself performs the work of rebuilding the user interface in response to user state change events. This is a typical responsive programming paradigm. Let's summarize the responsive principles in React below:
- Developers only need to pay attention to the state transfer (data). When the state changes, the React framework will automatically rebuild the UI according to the new state.
- After receiving the user state change notification, the React framework will calculate the changed part of the tree through the Diff algorithm based on the current rendering tree and the latest state change, and then only update the changed part (DOM operation), thus avoiding the entire tree Tree refactoring to improve performance.
It is worth noting that in the second step, after the state changes, the React framework will not immediately calculate and render the changed part of the DOM tree. On the contrary, React will build an abstract layer on the basis of the DOM tree, that is, the virtual DOM tree . Any changes made to data and state will be automatically and efficiently synchronized to the virtual DOM, and finally synchronized to the real DOM in batches, instead of manipulating the DOM every time there is a change.
Why can't we directly manipulate the DOM tree every time it changes? This is because every DOM operation in the browser may cause the browser to redraw or reflow (re-typesetting the layout, determining the size and position of the DOM node):
- If the DOM only changes in appearance and style, such as a color change, it will cause the browser to redraw the interface.
- If the structure of the DOM tree changes, such as size, layout, hidden nodes, etc., the browser needs to reflow.
The redrawing and reflow of the browser are relatively expensive operations. If each change directly operates on the DOM, this will cause performance problems. However, batch operations will only trigger a DOM update, which will have higher performance.
React Native
React Native (RN for short) is a cross-platform mobile application development framework open sourced by Facebook in April 2015. It is a derivative of Facebook's earlier open source Web framework React on the native mobile application platform. It currently supports both iOS and Android platforms. RN uses the JSX language (extended JavaScript, mainly to write HTML tags in JavaScript) and CSS to develop mobile applications. Therefore, technicians who are familiar with web front-end development can enter the field of mobile application development with very little learning.
Since the principles of RN and React are similar, and Flutter is also inspired by React at the application layer, many ideas are also similar.
As mentioned above, React Native is a derivative of React on the native mobile application platform. What is the main difference between the two? Actually, the main difference is what objects are mapped by the virtual DOM. The virtual DOM in React will eventually be mapped to the browser DOM tree, while the virtual DOM in RN will be mapped to native controls through JavaScriptCore.
JavaScriptCore is a JavaScript interpreter, it has two main functions in React Native:
-
Provides a runtime environment for JavaScript.
-
It is a communication bridge between JavaScript and native applications, and its function is the same as that of JsBridge. In fact, in iOS, many implementations of JsBridge are based on JavaScriptCore.
The process of mapping virtual DOM to native controls in RN is mainly divided into two steps: -
Layout message passing; pass virtual DOM layout information to native;
-
Native rendering through the corresponding native controls according to the layout information;
So far, React Native has achieved cross-platform. Compared with hybrid applications, since React Native renders native controls, the performance will be better than that of H5 in hybrid applications. At the same time, React Native provides many web components corresponding to native components. In most cases, developers only need to use the Web technology stack. Can develop App. We can find that in this way, one code can be maintained, and it can be cross-platform.
Weex
Weex is a cross-platform mobile development framework released by Alibaba in 2016. Its ideas and principles are similar to React Native. The bottom layer is rendered natively. The difference is the application layer development syntax (ie DSL, Domain Specific Language): Weex supports Vue Grammar and Rax syntax, Rax's DSL (Domain Specific Language) syntax is created based on React JSX syntax, while RN's DSL is based on React and does not support Vue.
summary
The main advantages of JavaScript development + native rendering are as follows:
- Using the web development technology stack, the community is huge, quick to learn, and the development cost is relatively low.
- Native rendering, the performance is much improved compared to H5.
- It is dynamic and supports hot updates.
insufficient:
- Communication between JavaScript and native is required during rendering. In some scenarios such as dragging, frequent communication may cause lag.
- JavaScript is a scripting language, which needs to be interpreted and executed during execution (this execution method is usually called JIT, or Just In Time, which refers to generating machine code in real time during execution), execution efficiency and compiled language (the execution method of compiled language is AOT , that is, Ahead Of Time, which means that the source code has been preprocessed before the code is executed. This preprocessing usually compiles the source code into machine code or some kind of intermediate code) and there is still a gap.
- Because rendering relies on native controls, controls on different platforms need to be maintained separately, and when the system is updated, community controls may lag behind; in addition, its control system will also be limited by the native UI system, for example, in Android, gesture conflicts The disambiguation rules are fixed, which makes the gesture conflict problem very difficult when using nested controls written by different people. This will lead to high development and maintenance costs if custom native rendering components are required.
Solution 3: Self-drawn UI + native
The idea of this technology is to draw the UI by implementing a rendering engine with a unified interface on different platforms, without relying on the native controls of the system, so the UI consistency of different platforms can be achieved.
Note that the self-drawing engine solves the cross-platform problem of UI. If other system capability calls are involved, native development is still involved. The advantages of this platform technology are as follows:
-
High performance; since the self-drawing engine directly calls the system API to draw the UI, the performance is close to that of native controls.
-
Flexible, easy to maintain component library, high fidelity and consistency of UI appearance; since UI rendering does not depend on native controls, there is no need to maintain a separate component library according to controls on different platforms, so the code is easy to maintain. Since the component library is the same set of code and the same rendering engine, the component display appearance can achieve high fidelity and high consistency on different platforms; in addition, because it does not rely on native controls, it will not be limited by the native layout system. This layout system will be very flexible.
insufficient:
- Insufficient dynamics; in order to ensure UI drawing performance, self-drawing UI systems generally use AOT mode to compile their release packages, so after the application is released, it cannot dynamically deliver code like Hybrid and RN frameworks that use JavaScript (JIT) as the development language .
- Low application development efficiency: Qt uses C++ as its development language, and programming efficiency will directly affect the efficiency of App development. As a static language, C++ is not as flexible as a dynamic language such as JavaScript in terms of UI development. In addition, C++ needs to be developed Or manually manage memory allocation, there is no garbage collection (GC) mechanism in JavaScript and Java.
You may have guessed that Flutter belongs to this type of cross-platform technology. Yes, Flutter implements a set of self-drawing engines and has its own UI layout system. At the same time, it has made great breakthroughs in development efficiency. However, the idea of a self-drawing engine is not a new concept. Flutter is not the first to try to do this. Before it, there was a typical representative, that is, the famous Qt.
Qt Mobile
Qt is a cross-platform C++ graphical user interface application development framework developed by the Qt Company in 1991. In 2008, Qt Company technology was acquired by Nokia, and Qt became a programming language tool under Nokia. In 2012, Qt was acquired by Digia. In April 2014, the cross-platform integrated development environment Qt Creator 3.1.0 was officially released, fully supporting iOS, adding WinRT, Beautifier and other plug-ins, abandoning the GDB debugging support without Python interface, and integrating Clang-based C /C++ code module, and made adjustments to Android support, so far fully supports iOS, Android, WP, it provides application developers with all the functions required to build a graphical user interface.
However, although Qt has achieved great success on the PC side and is highly sought after by the community, it has not performed well on the mobile side. In recent years, the voice of Qt has rarely been heard, even though Qt is a cross-platform self-drawing platform for mobile development. The pioneer of the engine, but became a martyr. The reasons may be as follows:
- The Qt mobile development community is too small, the learning materials are insufficient, and the ecology is not good.
- The official promotion is not good and the support is not enough.
- The mobile terminal started late, and the market has been occupied by other dynamic frameworks (Hybrid and RN).
- In mobile development, C++ development has an inherent disadvantage compared with the Web development stack, and the direct result is that the efficiency of Qt development is too low.
Flutter
Flutter is a framework released by Google for creating cross-platform, high-performance mobile applications. Flutter, like Qt mobile, does not use native controls. On the contrary, they both implement a self-drawing engine and use their own layout and drawing system. Then, we will worry, whether the problems faced by Qt mobile are the same as Flutter, will Flutter step into the footsteps of Qt mobile and become another martyr? Since Flutter was born in 2017, after years of experience, the Flutter ecosystem has grown rapidly. There are many successful cases based on Flutter at home and abroad, and domestic Internet companies basically have dedicated Flutter teams. In short, Flutter has developed rapidly, has received widespread attention and recognition in the industry, and has been warmly welcomed by developers, becoming one of the most popular frameworks in mobile cross-end development.
Now, let's make a comparison with Qt mobile:
- Ecology: The Flutter ecosystem is developing rapidly, and the community is very active. Both the number of developers and third-party components are already very impressive.
- Technical support: Now Google is vigorously promoting Flutter. Many of the authors of Flutter are from the Chromium team, and they are very active on Github. From another perspective, from the birth of Flutter to the present, frequent version releases also show that Google has invested a lot of resources in Flutter, so there is no need to worry about official technical support.
- Development efficiency: One set of code, multi-terminal operation; and during the development process, Flutter's hot reload can help developers quickly test, build UI, add functions and fix errors faster. Millisecond-level hot reloads can be achieved on iOS and Android simulators or real devices without losing state. This is really great. Believe me, if you are a native developer, after experiencing the Flutter development flow, you probably don’t want to go back to doing native again. After all, few people don’t complain about the compilation speed of native development.
GC in Flutter
Flutter uses Dart as the development language and runtime mechanism. Dart has always retained the runtime mechanism, whether in debug mode (debug) or release mode (release), but there are big differences between the two construction methods.
- In debug mode, Dart loads all pipelines (all accessories that need to be used) on the device: Dart runtime , JIT (the just-in-time) compiler/interpreter (JIT for Android and interpreter for iOS) , debugging and performance analysis services.
- In release mode, AOT compilation will be used, JIT will be removed , and the Dart runtime will still be retained, because the Dart runtime is the main contributor to the Flutter App.
Dart's runtime includes a very important component: the garbage collector, whose main role is to allocate and release memory when an object is instantiated or becomes unreachable.
During the running of Flutter, there will be many Objects. They are created before (actually still) rendering StatelessWidget
. StatefulWidget
When the state changes, they will be destroyed again. In fact, they have a very short lifespan. When we build a complex UI interface, there will be thousands of these Widget
.
So, as Flutter developers, do we need to worry about whether the garbage collector can help us manage these well? (Will it bring a lot of performance problems?) As Flutter frequently creates and destroys these Widget
(Objects) objects, should developers take steps to limit this behavior?
- For new Flutter developers, if they know a Widget won't change over time, they create a
Widget
reference and put themState
in so they don't get destroyed and rebuilt, which doesn't Not uncommon.
no need to do that
- It is completely unnecessary to worry about Dart's GC, because its generational architecture is specially optimized to allow us to create and destroy objects frequently. In most cases, we just need to let Flutter's engine create and destroy all it likes
Widget
.
Dart GC
Dart's GC is generational (generational) and consists of two stages: Young Space Scavenger (scavenger recycles for the young bag) and Parallel Marking and Concurrent Sweeping (sweep collectors recycles for the old generation)
Scheduling
To minimize the impact of GC on application and UI performance, the garbage collector provides hooks to the Flutter engine that will alert it when the engine detects that the application is idle with no user interaction. This gives the garbage collector window a chance to run its collection phase without affecting performance.
The garbage collector can also run sliding compaction during these idle intervals, which minimizes memory overhead by reducing memory fragmentation.
Phase 1: Young Space Scavenger
This stage is mainly to clean up some short-lived objects, such as StatelessWidget
. When it is blocked, its cleaning speed is much faster than the second-generation mark/sweep method. And combined with scheduling, completion can eliminate the pause phenomenon when the program is running.
Essentially, objects are allocated to a contiguous space in memory, and when objects are created, they are allocated to the next available space until the allocated memory is filled. Dart uses bump pointer allocation to quickly allocate new space, making the process very fast. (If you maintain free_list and redistribute like malloc, the efficiency is very low)
The new space in which new objects are allocated consists of two halves, called half-spaces. Only half of the space is used at any one time: one half is active and the other half is inactive. New objects are allocated to the active half of the area, and once the active half is filled, the active objects are copied from the active area to the inactive area, and dead objects are cleared. The inactive half then becomes active and the process repeats. (This is very similar to the survivor space of the new generation in the JVM's generational recycling strategy)
To determine which objects are alive or dead, the GC starts with root objects (such as stack variables) and examines what they refer to. Then move the referenced Object (survival) to the inactive state, and directly all surviving Objects are moved. Dead Objects have no references and are thus left around; live objects will be copied over them in future garbage collection events.
For more information on this, check out Cheney's Algorithm.
Phase 2: Parallel Marking and Concurrent Sweeping
When objects reach a certain age (not reclaimed by GC in the first phase), they will be promoted to a new memory space managed by the second-generation collector: mark-sweep.
This garbage collection technique has two phases: first traversing the object graph, and then marking objects that are still in use. In the second phase, the entire memory is scanned and any unmarked objects are reclaimed. Then clear all flags.
This GC technique blocks the marking phase; memory mutations do not occur, and the UI thread is also blocked. But since ephemeral objects are already processed in the Young Space Scavenger phase, this situation is very rare. But sometimes the Dart runtime needs to be paused to run this form of GC. Considering the capabilities of Flutter's planned collection, its impact should be minimized.
It is important to note that this form of GC will occur more frequently if an application does not follow the generational assumption (i.e. the assumption that most objects die young). Given Widget
how things work in Flutter, this is unlikely to happen, but something to know.
Isolate
It is worth noting that the mechanisms in Dart Isolate
have the concept of a private heap , independent of each other . Each Isolate
has its own separate thread to run, and Isolate
the GC of each does not affect Isolate
the performance of the other. Using Isolate
is a great way to avoid blocking the UI and offload process-intensive activities. (Time-consuming operations can use Isolate)
You should understand here: Dart uses a powerful generational garbage collector to minimize the performance impact of GC in Flutter. So, you don't need to worry about Dart's garbage collector, and you can focus on your business with confidence.
reference: