Flutter是怎么構(gòu)建一個視圖頁面的碰凶,Widget是如何繪制到屏幕上的欲低,這涉及到三棵樹:
- Widget Tree
- Element Tree
- RenderObject Tree
Flutter入口函數(shù)為main()函數(shù)
void main()=> run(new MyApp());//MyApp是一個Widget
runApp 函數(shù)接收一個Widget類型的對象作為參數(shù)砾莱,也就可以理解為萬物皆為Widget,其他的業(yè)務(wù)邏輯等都只是在為Widget的數(shù)據(jù)腊瑟,狀態(tài)改變而服務(wù),下面我們看看runApp里面都做了些什么:
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..attachRootWidget(app)//把傳進(jìn)來的Widget掛載到跟Widget
..scheduleWarmUpFrame();//主動構(gòu)建視圖
}
class WidgetsFlutterBinding extends BindingBase with GestureBinding, ServicesBinding, SchedulerBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
//單例
static WidgetsBinding ensureInitialized() {
if (WidgetsBinding.instance == null)
WidgetsFlutterBinding();
return WidgetsBinding.instance;
}
}
在runApp中會實(shí)例化一個WidgetsFlutterBinding單例闰非,然后將傳進(jìn)來的Widget掛載到跟Widget上峭范,WidgetsFlutterBinding通過mixin來使用框架中實(shí)現(xiàn)的其他 binding的 Service纱控,比如 手勢、基礎(chǔ)服務(wù)舶掖、隊(duì)列唾那、繪圖等
接下來我們看看attachRootWidget方法做了什么:
// Element
Element _renderViewElement;
void attachRootWidget(Widget rootWidget) {
_renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
container: renderView,
debugShortDescription: '[root]',
child: rootWidget,
).attachToRenderTree(buildOwner, renderViewElement);
}
attachRootWidget把 widget交給了 RenderObjectToWidgetAdapter這個適配器闹获,通過attachRootWidget避诽,Element被創(chuàng)建沙庐,并且同時(shí)能持有 Widget和 RenderObject的引用拱雏。然后我們看看attachToRenderTree做了什么:
RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T> element ]) {
if (element == null) {
owner.lockState(() {
element = createElement();
assert(element != null);
element.assignOwner(owner);
});
owner.buildScope(element, () {
element.mount(null, null);
});
} else {
element._newWidget = this;
element.markNeedsBuild();
}
return element;
}
從源碼中我們能看到如果element是空贡耽,則調(diào)用createElement方法去創(chuàng)建蒲赂,然后通過mount方法將其掛載到視圖樹上刁憋。但是走到這我們都不知道Widget是怎么被畫出來的,只是大概了解到當(dāng)當(dāng)一個Widget首次被創(chuàng)建的時(shí)候至耻,那么這個Widget會過Widget.createElement inflate成一個element,掛在 element tree 上〕就牵現(xiàn)在我們看一個簡單的控件Opacity(設(shè)置控件的不透明度,取值[0,1])
- Opacity 繼承關(guān)系
Opacity extends SingleChildRenderObjectWidget extends RenderObjectWidget extends Widget
- StatelessWidget 繼承關(guān)系
StatelessWidget extends Widget
- StatefulWidget 繼承關(guān)系
StatefulWidget extends Widget
Opacity 比StatelessWidget,StatefulWidget多了 SingleChildRenderObjectWidget泥耀,RenderObjectWidget兩層繼承關(guān)系
- RenderObjectWidget 源碼
/// RenderObjectWidgets provide the configuration for [RenderObjectElement]s,
/// which wrap [RenderObject]s, which provide the actual rendering of the
/// application.
//半吊子注釋: RenderObjectWidgets 為 [RenderObjectElement]s提供配置,而真正為應(yīng)用渲染視圖的的是包裹Widget的 [RenderObject]s痰催,所以RenderObject 才是實(shí)際繪制視圖的對象
abstract class RenderObjectWidget extends Widget {
//構(gòu)造
const RenderObjectWidget({ Key key }) : super(key: key);
/// RenderObjectWidgets always inflate to a [RenderObjectElement] subclass.
//RenderObjectWidgets 一直填充于一個 RenderObjectElement 的子類兜辞,創(chuàng)建element對象
@override
RenderObjectElement createElement();
/// Creates an instance of the [RenderObject] class that this
/// [RenderObjectWidget] represents, using the configuration described by this
/// [RenderObjectWidget].
///
/// This method should not do anything with the children of the render object.
/// That should instead be handled by the method that overrides
/// [RenderObjectElement.mount] in the object rendered by this object's
/// [createElement] method. See, for example,
/// [SingleChildRenderObjectElement.mount].
@protected
RenderObject createRenderObject(BuildContext context);
....
}
通過此類可以知道Widget為Element提供配置,RenderObject真正繪制視圖。還有一個方法就是createRenderObject(BuildContext context),看其注釋夸溶,此方法返回一個RenderObject實(shí)例逸吵,去描述(表現(xiàn))RenderObjectWidget的配置信息缝裁。此方法不應(yīng)對render對象的子代執(zhí)行任何操作。
而是由可覆蓋的RenderObjectElement.mount方法調(diào)用處理,例如SingleChildRenderObjectElement中的mount方法韩脑。
看到這里讓我想起之前 attachRootWidget 這個方法的源碼,再貼一次:
RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T> element ]) {
if (element == null) {
owner.lockState(() {
element = createElement();
assert(element != null);
element.assignOwner(owner);
});
owner.buildScope(element, () {
element.mount(null, null);
});
} else {
element._newWidget = this;
element.markNeedsBuild();
}
return element;
}
可以看到attachToRenderTree中element調(diào)用mount()方法进苍,mount 方法實(shí)例化一個RenderObject觉啊,由RenderObject 對象繪制視圖。還是接著看SingleChildRenderObjectWidget類:
abstract class SingleChildRenderObjectWidget extends RenderObjectWidget {
const SingleChildRenderObjectWidget({ Key key, this.child }) : super(key: key);
final Widget child;
// 重寫 createElement杠人,返回 SingleChildRenderObjectElement 實(shí)例對象
@override
SingleChildRenderObjectElement createElement() => SingleChildRenderObjectElement(this);
}
接著看 SingleChildRenderObjectElement類:
class SingleChildRenderObjectElement extends RenderObjectElement {
Element _child;
//重寫 的mount方法
@override
void mount(Element parent, dynamic newSlot) {
super.mount(parent, newSlot);
_child = updateChild(_child, widget.child, null);
}
}
- super.mount(parent, newSlot)
RenderObject _renderObject;
@override
void mount(Element parent, dynamic newSlot) {
super.mount(parent, newSlot);
//創(chuàng)建_renderObject實(shí)例對象
_renderObject = widget.createRenderObject(this);
assert(() { _debugUpdateRenderObjectOwner(); return true; }());
assert(_slot == newSlot);
attachRenderObject(newSlot);
_dirty = false;
}
由此知道蚀浆,當(dāng)調(diào)用mount掛載的時(shí)候,會調(diào)用createRenderObject生成_renderObject實(shí)例搜吧。而createRenderObject 方法我們可以在Opacity這個組件類里看到市俊,它返回了一個RenderOpacity的實(shí)例:
@override
RenderOpacity createRenderObject(BuildContext context) {
return RenderOpacity(
opacity: opacity,
alwaysIncludeSemantics: alwaysIncludeSemantics,
);
}
在RenderOpacity中,重寫paint() 方法滤奈,調(diào)用context.pushOpacity繪制視圖,控制透明度:
void paint(PaintingContext context, Offset offset) {
if (child != null) {
if (_alpha == 0) {
return;
}
if (_alpha == 255) {
context.paintChild(child, offset);
return;
}
assert(needsCompositing);
context.pushOpacity(offset, _alpha, super.paint);
}
}
小結(jié)
調(diào)用runApp(rootWidget)摆昧,將rootWidget傳給rootElement,做為rootElement的子節(jié)點(diǎn),生成Element樹,由Element樹生成Render樹
Widget:存放渲染內(nèi)容蜒程、視圖布局信息,widget的屬性最好都是immutable(一成不變的)
Element:存放上下文绅你,通過Element遍歷視圖樹,Element同時(shí)持有Widget和RenderObject
RenderObject:根據(jù)Widget的布局屬性進(jìn)行l(wèi)ayout昭躺,paint Widget傳入的內(nèi)容
在第一次創(chuàng)建 Widget 的時(shí)候忌锯,會對應(yīng)創(chuàng)建一個 Element, 然后將該元素插入樹中领炫。如果之后 Widget 發(fā)生了變化偶垮,則將其與舊的 Widget 進(jìn)行比較,并且相應(yīng)地更新 Element帝洪。重要的是似舵,Element 被不會重建,只是更新而已葱峡。這個目前的我還沒看相關(guān)的源碼砚哗。。砰奕。
代碼
class ElementApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(home: OpacityHome(),theme: ThemeData.light(),);
}
}
class OpacityHome extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("flutter繪制機(jī)制"),),
body: Center(
child: Opacity(opacity: 0.5,child: Container(
width: 100,
color: Colors.black87,
alignment: Alignment.center,
height: 100,
child: Text("flutter",style: TextStyle(fontSize: 20,color: Colors.white),),
),),
),
);
}
}
效果圖
看到這雖然對flutter繪制有一定了解蛛芥,但是好多問題出現(xiàn)了:
- 視圖的更新機(jī)制提鸟,更新的依據(jù)是什么樣?
- BuildContext 又有啥作用仅淑?
- Widget中的Key又有啥作用称勋?
如果覺得有什么錯誤,歡迎拍磚漓糙。。烘嘱。