Flutter is Google’s portable UI toolkit for building beautiful, natively-compiled applications for mobile, web, and desktopfrom a single codebase.

Fluter 是 Google 的便携式 UI 工具包,用于从单个代码库构建美观、本地编译的移动、Web 和桌面应用程序。

image.png

Flutter 的框架使用 Dart 实现,提供了 Material 风格的小部件、Cupertino 风格的小部件(用于 iOS)、文本 / 图像 / 按钮小部件、渲染、动画、手势等。该层的核心代码包含了 flutter 代码库的包和 sky_engine 代码库的包(dart:ui 库提供了 flutter 框架和引擎之间的接口),例如 io、async 和 ui 包。

Flutter 的引擎是用 C++ 实现的,并包含了 Skia、Dart 和 Text。Skia 是一个开源的 2D 图形库,为各种硬件和软件平台提供通用 API。它是谷歌 Chrome、Chrome OS、Android、Mozilla Firefox、Firefox OS 等产品的图形引擎。支持的平台包括 Windows7+、macOS 10.10.5+、iOS8+、Android4.1+、Ubuntu14.04+ 等。
引擎的 Dart 部分主要包括 Dart 运行时和垃圾回收(GC)。如果 Flutter 在调试模式下运行,则还包括 JIT(Just in Time)支持,而如果是在发布模式下,则通过 AOT(Ahead of Time)将 Dart 代码编译为原生的“arm”代码,这个时候就没有 JIT。Text 是指以下的文本渲染库:libtxt 库(用于字体选择和分隔线),派生自 minikin 和 HartBuzz(用于字形和图形)。Skia 充当渲染后端,在 Android 上使用 FreeType 渲染,在 iOS 上使用 Fuchsia 和 CoreGraphics 渲染。

嵌入器可将 Flutter 嵌入到各种平台中。它的主要任务是渲染 Surface 设置、线程设置和插件。我们可以看到,Flutter 的平台相关层是最小的,其中平台(例如 iOS)只提供画布,其余与渲染相关的逻辑发生在 Flutter 内部,从而实现良好的跨平台一致性。

项目结构:
image.pngimage.pngimage.png

lib:程序代码 dart源程序
android:android运行时环境
ios:ios运行环境

一切都是widget

Flutter官方文档里的一句话:you build your UI out of widgets(使用Flutter开发UI界面时,都是使用Widget)
然而,Widget并不是我们真正看到的视图,背后究竟是什么?其实Flutter Framework提供了三种视图树,即:Widget Element RenderObject,只不过,我们使用Flutter开发界面时,通常只和widget打交道,Materail风格或者Cupertino(IOS风格)的各种Widget

Widget的定义和描述:Describes the configuration for an Element. (为Element提供配置信息)

Widget是用户界面的一部分,并且是不可变的(immutable)。Widget会被inflate到Element,并由Element管理底层渲染树。Widget本身没有可变状态(所有的字段必须是final)。如果想要把可变状态与Widget关联起来,可以使用StatefulWidget,StatefulWidget通过使用StatefulWidget.createState方法创建State对象,并将之扩充到Element以及合并到树中;一个给定的Widget可以放置在树中多次,比如:多个TextWidget。每次将一个Widget放入树中时,它都会被扩充到一个Element中,这也意味着多次并入树中的Widget将会被多次扩充进对应的element。

widget内置属性
1:KEY key
2:int hasCode
3:TYPE runtimeType
Widget中的Key这个属性控制一个Widget如何替换树中的另一个Widget。如果两个Widget的runtimeType和key属性相等(==),则新的widget通过更新Element(即通过使用新的Widget调用Element.update)来替换旧的Widget。否则,如果两个Widget的runtimeType和key属性不相等,则旧的Element将从树中被移除,新的Widget将被扩充到一个新的Element中,这个新的Element将被插入树中。

InheritedWidget

InheritedWidget既不是StatefullWidget也不是StatelessWidget。它是用来向下传递数据的。在InheritedWidget之下的子节点都可以通过调用BuildContext.inheritFromWidgetOfExactType()来获取这个InheritedWidget。它的createElement()函数返回的是一个InheritedElement

Element树

Element的属性中看到renderObject和widget,说明Element持有这两个类的实例;
再来看看文档中对于Element的介绍:An instantiation of a Widget at a particular location in the tree. Element是在树中特定位置Widget的实例;
这里进一步描述了Widget与Element之间的关系:Element是Widget的实例

布局类Widget都会包含一个或多个子widget,不同的布局类Widget对子widget排版(layout)方式不同。我们在前面说过Element树才是最终的绘制树,Element树是通过widget树来创建的(通过Widget.createElement()),widget其实就是Element的配置数据

image.png

image.png

widget 与 element

image.png

敲重点:
UI 更新 也就是 当我们布局完成 statefullWidget 调用setState 设置element需要重建时候 到底widget和对应的element 做了什么 。

widget的初始化以及element的装载流程:

入口开始: runApp(new MyApp());
/// [WidgetsBinding.attachRootWidget], which creates the root widget for the/// widget hierarchy./// [RenderObjectToWidgetAdapter.attachToRenderTree], which creates the root///
element for the element hierarchy.
/// * [WidgetsBinding.handleBeginFrame], which pumps the widget pipeline to///
ensure the widget, element, and render trees are all built.
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..attachRootWidget(app)
..scheduleWarmUpFrame();
}

_attachRootWidget:创建了widget树的根结点 ~ 以及 element 根结点_RenderObjectToWidgetElement
去执行element.mount() 继而绑定widget和element 通过scheduleWarmUpFrame唤醒执行绘制

widget的更新和element的更新和复用:

在State类中的调用setState((){})更新视图,执行markNeedsBuild 把当前element标记为dirty 并通过
scheduleBuildFor把当前element添加到elements dirty list中 ,当WidgetsBinding.drawFrame 执行下一贞时候
执行buildScope,— rebuild() —ComponentElement.performRebuild()—对于statefullWidget(执行statefuullElement.state.build 通过state rebuild创建widget )对于stateLessWidget(执行stateLessElement.build()直接返回新的widget)然后updateChild()更新所有子节点。

Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
assert(() {
if (newWidget != null && newWidget.key is GlobalKey) {
final GlobalKey key = newWidget.key;
key._debugReserveFor(this);
}
return true;
}());
if (newWidget == null) {//1.如果刚build出来的widget等于null,说明这个控件被删除了,child Element可以被删除了。
if (child != null)
deactivateChild(child);//
return null;
}
if (child != null) {
//如果child的widget和新build出来的一样(Widget复用了),就看下位置一样不,不一样就更新下,一样就直接return了。Element还是旧的Element
if (child.widget == newWidget) {
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);//卸载
return child;
}

//看下Widget是否可以update,Widget.canUpdate的逻辑是判断key值和运行时类型是否相等。如果满足条件的话,就更新,并返回。

只要新build出来的Widget和上一次的类型和Key值相同,Element就会被复用!由此也就保证了虽然Widget在不停的新建,但只要不发生大的变化,那Element是相对稳定的,也就保证了RenderObject是稳定的!

if (Widget.canUpdate(child.widget, newWidget)) {// key runtimetype一致
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);//卸载
child.update(newWidget);//更新
assert(child.widget == newWidget);
assert(() {
child.
owner._debugElementWasRebuilt(child);
return true;
}());
return child;
}
deactivateChild(child);//卸载
assert(child._parent == null);
}
//inflateWidget首先会尝试通过GlobalKey去查找可复用的Element,复用失败就调用Widget的方法创建新的Element,然后调用mount方法,将自己挂载到父Element上去,mount之前我们也讲过,会在这个方法里创建新的RenderObject。
return **inflateWidget(newWidget, newSlot);//更新创建新的element 并装载
}

将会触发State.build! 也将间接的触发其每个子Widget的构造方法以及build方法。
这意味这什么呢? 如果你的根布局是一个StatefulWidget,那么每在根State中调用一次setState((){}),都将是一次整页所有Widget的rebuild!!!~ (google 号称widget是轻量级的 优化过的 widget的大量重建不会导致性能问题。实质也是指widget实质内存中存在的配置对象,不参与界面的绘制。而element树则是对应了屏幕绘制的复杂度)。好在element树会根据widget更新或者重新创建新的,尽最大复用element保证element树的稳定。但还是要注意内存不能浪费啊~
到这里 基本widget与element的关系和element的更新流程基本清楚了。所关注的就是如何尽量小的避免element的大量创建造成的内存和性能开销。
Flutter可以在某些节点设置布局边界(Relayout boundary),即当边界内的任何对象发生重新布局时,不会影响边界外的对象,不引发边界外的ui重构。

**

https://flutter.dev/
https://www.jianshu.com/p/c51149cb85e4
https://www.jianshu.com/p/f39cf2f7ad78 widget 生命周期
https://www.jianshu.com/p/e9f48141218d?tdsourcetag=s_pctim_aiomsg widget 重建与element的更新复用
https://lizhaoxuan.github.io/2019/01/02/Flutter-%E4%BD%A0%E8%BF%98%E5%9C%A8%E6%BB%A5%E7%94%A8StatefulWidget%E5%90%97/(stateless 与 statefull 的使用注意事项)
https://www.jianshu.com/p/3cf13700e711