
class Tree extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
color: Colors.brown,
child: Row(
children: [
new Image.network(
"http://www.kaotop.com/file/tupian/20220518/t01d5ccfbf9d4500c75.jpg",
width: 100,
height: 100,
),
new Text(
"从网络加载图片",
style: TextStyle(
fontSize: 16
),
),
],
),
);
}
}
当runApp()被调用时,第一时间会在后台发生以下事件:
Flutter会构建包含Widget(Container,Row,Image,Text)的Widgets树;Flutter遍历Widget树,然后根据其中的Widget调用createElement()来创建相应的Element对象,最后将这些对象组建成Element树;接下来会创建第三个树,这个树中包含了与Widget对应的Element通过createRenderObject()创建的RenderObject; 具体Flutter经过这三个步骤后的状态
总结一下三棵树结构
Widget Tree: Widget 是 Flutter 面向开发者的上层接口,我们通过 widget 的层层嵌套,会形成一颗 Widget 树,一个 Widget 可在多个位置复用。Flutter Framework 层为我们提供了一些常用的包装或者容器的 Widget,比如 Container,其内部继续嵌套了其他 Widget,如 Padding、Align 等等。所以,开发者编写的 Widget 树和实际生成的 Widget 树都会略有差别。如图中虚线圆形标注的 ColorBox、RawImage 等。Element Tree :每一个 Widget 都会对应一个 Element,只不过 Element 分类不同。RenderObject Tree:RenderObject 只负责最终的测量、布局和绘制,因此最终的 RenderObject Tree 是 Element Tree 剔除掉哪些包装,最后组织而成的 Tree。
4.2 为何搞这多树
分层:开发只关注widget
Framework 将复杂的内部设计、渲染逻辑与开发接口隔离开,应用层只需关注 Widget 开发即可。 高效:提交绘制效率
Tree 最大的共同特点就是快取,因为 Element、RenderObject 销毁重建成本很高,一旦可以复用 ,那么快取可以大幅减少这种开销。比如:当 Element 不需要重建时,更新 Widget 的引用就可以了;Layer Tree 的设计是将绘制图层分开,方便提取和合成,合成层中的 transform 和 opacity 效果,都只是几何变换、透明度变换等,不会触发 layout 和 paint,直接由 GPU 完成即可。
05.三棵树的作用介绍
5.1 简单了解更新 *** 作
简而言之是为了性能,为了复用Element从而减少频繁创建和销毁RenderObject。
因为实例化一个RenderObject的成本是很高的,频繁的实例化和销毁RenderObject对性能的影响比较大,所以当Widget树改变的时候,Flutter使用Element树来比较新的Widget树和原来的Widget树: //framework.dart
@protected
Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
if (newWidget == null) {
if (child != null)
deactivateChild(child);
return null;
}
Element newChild;
if (child != null) {
assert(() {
final int oldElementClass = Element._debugConcreteSubtype(child);
final int newWidgetClass = Widget._debugConcreteSubtype(newWidget);
hasSameSuperclass = oldElementClass == newWidgetClass;
return true;
}());
if (hasSameSuperclass && child.widget == newWidget) {
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
newChild = child;
} else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) {
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
child.update(newWidget);
assert(child.widget == newWidget);
assert(() {
child.owner._debugElementWasRebuilt(child);
return true;
}());
newChild = child;
} else {
deactivateChild(child);
assert(child._parent == null);
newChild = inflateWidget(newWidget, newSlot);
}
} else {
newChild = inflateWidget(newWidget, newSlot);
}
assert(() {
if (child != null)
_debugRemoveGlobalKeyReservation(child);
final Key key = newWidget?.key;
if (key is GlobalKey) {
key._debugReserveFor(this, newChild);
}
return true;
}());
return newChild;
}
...
static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType
&& oldWidget.key == newWidget.key;
}
如果某一个位置的Widget和新Widget不一致,才需要重新创建Element;如果某一个位置的Widget和新Widget一致时(两个widget相等或runtimeType与key相等),则只需要修改RenderObject的配置,不用进行耗费性能的RenderObject的实例化工作了;
因为Widget是非常轻量级的,实例化耗费的性能很少,所以它是描述APP的状态(也就是configuration)的最好工具;重量级的RenderObject(创建十分耗费性能)则需要尽可能少的创建,并尽可能的复用;
5.2 更新时三棵树 *** 作
因为Widget是不可变的,当某个Widget的配置改变的时候,整个Widget树都需要被重建。
例如当我们改变一个Text文本的时候,框架就会触发一个重建整个Widget树的动作。因为有了Element的存在,Flutter会比较新的Widget树中的第一个Widget和之前的Widget。接下来比较Widget树中之后Widget和之前Widget,以此类推,直到Widget树比较完成。 @override
Widget build(BuildContext context) {
return Container(
color: Colors.brown,
height: double.infinity,
child: Row(
children: [
new Image.network(
"http://www.kaotop.com/file/tupian/20220518/t01d5ccfbf9d4500c75.jpg",
width: 100,
height: 100,
),
new Text(
"改变UI",
style: TextStyle(
fontSize: 16
),
),
],
),
);
}
Flutter遵循一个最基本的原则:判断新的Widget和老的Widget是否是同一个类型:
如果不是同一个类型,那就把Widget、Element、RenderObject分别从它们的树(包括它们的子树)上移除,然后创建新的对象;如果是一个类型,那就仅仅修改RenderObject中的配置,然后继续向下遍历。
推荐:https://github.com/yangchong211/YCFlutterUtils欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)