通過以前的學習,能夠知道Flutter是一種全新的響應式跨平臺的移動開發框架,愈來愈多的開發者參與學習或者研究中,在iOS和Android平臺上可以用一套代碼構建出性能比較高的應用程序。我剛開始接觸FlutterFlutter中文網看到這麼一句話:Widget
是Flutter應用程序用戶界面的基本構建塊。每一個Widget
都是用戶界面一部分的不可變聲明。與其餘將視圖、控制器、佈局和其餘屬性分離的框架不一樣,Flutter具備一致的統一對象模型:Widget。在開發過程當中也能夠知道Widget
能夠被定義按鈕(button)、樣式(style)、填充(Padding)、佈局(Row)、手勢(GestureDetector)等,我剛開始覺得這個Widget
就是眼中所看到的視圖,然而並非這樣的,下面慢慢講述。html
在Flutter官方網站介紹Widgets
開篇有這麼一段話:java
Flutter widgets are built using a modern react-style framework, which takes inspiration from React. The central idea is that you build your UI out of widgets. Widgets describe what their view should look like given their current configuration and state. When a widget’s state changes, the widget rebuilds its description, which the framework diffs against the previous description in order to determine the minimal changes needed in the underlying render tree to transition from one state to the next.node
這段話的意思是:Flutter widgets
是採起React思想使用響應式框架構建的。核心思想就是使用widgets
構建出UI(界面)。Widgets
根據其當前配置和狀態描述了它們的視圖。當某個widget
的狀態發生更改時,widget
會從新構建所描述的視圖,framework會根據前面所描述的視圖(狀態沒改變時)進行區分,以肯定底層呈現樹從一個狀態轉換到下一個狀態所需的最小更改步驟。react
在Flutter開發者文檔對Widget
的定義以下:bootstrap
Describes the configuration for an Element.api
Widgets are the central class hierarchy in the Flutter framework. A widget is an immutable description of part of a user interface. Widgets can be inflated into elements, which manage the underlying render tree.緩存
意思是:widget
爲element
(下面再描述)提供配置信息,這裏能夠知道widget
和element
存在某種聯繫。Widgets
在Flutter framework是中心類層次結構,widget
是不可變的對象而且是界面的一部分,widget
會被渲染在elements
上,並(elelments)管理底層渲染樹(render tree),這裏能夠獲得一個信息:widget
在渲染的時候會最終轉換成element
。繼續往下看:架構
Widgets themselves have no mutable state (all their fields must be final). If you wish to associate mutable state with a widget, consider using a StatefulWidget, which creates a State object (via StatefulWidget.createState) whenever it is inflated into an element and incorporated into the tree.app
意思是:Wigets
自己是沒有可變的狀態(其全部的字段必須是final)。若是你想吧可變狀態和一個widget
關聯起來,可使用StatefulWidget
,StatefulWidget
經過使用StatefulWidget.createState
方法建立State
對象,而且擴充到element
和合併到樹中。那麼這段能夠得出的信息是:widget
並不會直接渲染和管理狀態,管理狀態是交給State
對象負責。繼續往下一段看:框架
A given widget can be included in the tree zero or more times. In particular a given widget can be placed in the tree multiple times. Each time a widget is placed in the tree, it is inflated into an Element, which means a widget that is incorporated into the tree multiple times will be inflated multiple times.
意思是:給定的widget
能夠零次或者屢次被包含在樹中,一個給定的widget
能夠屢次放置在樹中,每次將一個widget
放入樹中,他都會被擴充到一個Element
,這就意味着屢次併入樹中的widget
將會屢次擴充到對應的Element
。這段能夠這麼理解:在一個界面中,有多個Text
被掛載在視圖樹上,這些Text
的widget
會被填充進本身獨立的Element
中,就算widget
被重複使用,仍是會建立多個不一樣的element
對象。繼續往下看:
The key property controls how one widget replaces another widget in the tree. If the runtimeType and key properties of the two widgets are operator==, respectively, then the new widget replaces the old widget by updating the underlying element (i.e., by calling Element.update with the new widget). Otherwise, the old element is removed from the tree, the new widget is inflated into an element, and the new element is inserted into the tree.
每個widget
都有本身的惟一的key,這裏也很容易理解,就是藉助key做爲惟一標識符。這段話的意思是:key這個屬性控制一個widget
如何替換樹中的另外一個widget
。若是兩個widget
的runtimeType和key屬性相等==,則新的widget
經過更新Element
(經過新的widget
來來調用Element.update)來替換舊的widget
。不然,若是兩個widget
的runtimeType和key屬性不相等,則舊的Element
將從樹中被移除,新的widget
將被擴充到一個新的Element
中,這個新的Element
將被插入樹中。這裏能夠得出:若是涉及到widget
的移動或者刪除操做前,會根據widget
的runtime和key進行對比。
綜上所述:
下面初步看看widget源碼:
@immutable
abstract class Widget extends DiagnosticableTree {
/// Initializes [key] for subclasses.
const Widget({ this.key });
//省略註釋
final Key key;
//構建出element
@protected
Element createElement();
//簡短文字描述這個widget
@override
String toStringShort() {
return key == null ? '$runtimeType' : '$runtimeType-$key';
}
//根據字面意思 應該是調試診斷樹的信息
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.defaultDiagnosticsTreeStyle = DiagnosticsTreeStyle.dense;
}
//靜態方法,跟上一段解釋同樣,就是是否用新的widget對象去更新舊UI渲染樹的配置
//若是oldWidget和newWidget的runtimeType和key同時相等就會用newWidget對象去更新對應element信息
static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType
&& oldWidget.key == newWidget.key;
}
}
複製代碼
還要注意:widget
是抽象類,在平時,通常繼續StatelessWidget
和StatefulWidget
,而這兩個類其實也是繼承Widget
,這兩個類確定會實現這個createElement
方法,簡單看一下:
StatelessWidget
abstract class StatelessWidget extends Widget {
/// Initializes [key] for subclasses.
const StatelessWidget({ Key key }) : super(key: key);
....
@override
StatelessElement createElement() => StatelessElement(this);
....
}
StatefulWidget
abstract class StatefulWidget extends Widget {
/// Initializes [key] for subclasses.
const StatefulWidget({ Key key }) : super(key: key);
.....
@override
StatefulElement createElement() => StatefulElement(this);
.....
}
複製代碼
確實能夠看出,都會建立Element
,只是StatelessWidget
和StatefulWidget
所建立的Element
類型不同,這裏就先不深刻了。上面能夠知道widget
和element
存在對應的關係,那下面看看element
。
看看官方開發者文檔中開篇看到:
An instantiation of a Widget at a particular location in the tree.
意思是:element
是樹中特定位置的widget
實例。這裏描述的很明顯,也就是Widget
是總監,部署技術規劃,而element
就是員工,真正幹活。繼續往下閱讀:
Widgets describe how to configure a subtree but the same widget can be used to configure multiple subtrees simultaneously because widgets are immutable. An Element represents the use of a widget to configure a specific location in the tree. Over time, the widget associated with a given element can change, for example, if the parent widget rebuilds and creates a new widget for this location.
意思是:widget
描述如何配置子樹,因爲widgets
是不可變的,因此能夠用相同的widget
來同時配置多個子樹,Element
表示widget
配置樹中的特定位置的實例,隨着時間的推移,和給定的Element
關聯的Widget
可能會隨時變化,例如,若是父widget
重建併爲此位置建立新的widget
。
Elements form a tree. Most elements have a unique child, but some widgets (e.g., subclasses of RenderObjectElement) can have multiple children.
Elements
構成一棵樹,大多數elements
都會有惟一的孩子,可是一些widgets
(如RenderObjectElement)能夠有多個孩子。
Elements have the following lifecycle:
- The framework creates an element by calling Widget.createElement on the widget that will be used as the element's initial configuration.
- The framework calls mount to add the newly created element to the tree at a given slot in a given parent. The mount method is responsible for inflating any child widgets and calling attachRenderObject as necessary to attach any associated render objects to the render tree.
- At this point, the element is considered "active" and might appear on screen.
- At some point, the parent might decide to change the widget used to configure this element, for example because the parent rebuilt with new state. When this happens, the framework will call update with the new widget. The new widget will always have the same runtimeType and key as old widget. If the parent wishes to change the runtimeType or key of the widget at this location in the tree, can do so by unmounting this element and inflating the new widget at this location.
- At some point, an ancestor might decide to remove this element (or an intermediate ancestor) from the tree, which the ancestor does by calling deactivateChild on itself. Deactivating the intermediate ancestor will remove that element's render object from the render tree and add this element to the owner's list of inactive elements, causing the framework to call deactivate on this element.
- At this point, the element is considered "inactive" and will not appear on screen. An element can remain in the inactive state only until the end of the current animation frame. At the end of the animation frame, any elements that are still inactive will be unmounted. *If the element gets reincorporated into the tree (e.g., because it or one of its ancestors has a global key that is reused), the framework will remove the element from the owner's list of inactive elements, call activate on the element, and reattach the element's render object to the render tree. (At this point, the element is again considered "active" and might appear on screen.)
- If the element does not get reincorporated into the tree by the end of the current animation frame, the framework will call unmount on the element. At this point, the element is considered "defunct" and will not be incorporated into the tree in the future.
- At this point, the element is considered "defunct" and will not be incorporated into the tree in the future.
意思以下: Element具備如下生命週期:
element
的初始化配置信息的Widget
的Widget.createElement方法來建立一個element
。Widget
擴充到Widget
並根據須要調用attachRenderObject
,以將任何關聯的渲染對象附加到渲染樹上。element
被視爲激活
,可能出如今屏幕上。Element
的Widget,例如由於父從新建立了新狀態。發生這種狀況時,framework將調用新的Widget
的update方法。新Widget
將始終具備與舊Widget
相同的runtimeType和key屬性。若是父但願在樹中的此位置更改Widget
的runtimeType或key,能夠經過unmounting(卸載)此Element並在此位置擴充新Widget
來實現。element
(或中間祖先),祖先本身經過調用deactivateChild來完成該操做。停用中間祖先將從渲染樹中移除該element
的渲染對象,並將此element
添加到全部者屬性中的非活動元素列表中,從而framework調用deactivate方法做用在此element
上。element
被視爲「無效」,不會出如今屏幕上。一個element
直到動畫幀結束前均可以保存「非活動」狀態。動畫幀結束時,將卸載仍處於非活動狀態的全部element
。element
被重寫組合到樹中(例如,由於它或其祖先之一有一個全局建(global key)被重用),framework將從全部者的非活動elements
列表中移除該element
,並調用該element
的activate方法,並從新附加到element
的渲染對象到渲染樹上。(此時,該元素再次被視爲「活動」並可能出如今屏幕上)element
在當前動畫幀的末尾(最後一幀)沒有被從新組合到樹中,那麼framework將會調用該元素的unmount方法。這裏能夠知道element
的生命週期。而且平時開發沒有接觸到Element
,都是直接操控widget
,也就是說Flutter已經幫咱們對widget
的操做映射到element
上,我這裏想象到的有點事下降開發複雜。下面結合一個例子(繪製Text),看看element
是否是最後渲染出來的view:
new Text("hello flutter");
複製代碼
下面看下new Text
的源碼:
...
@override
Widget build(BuildContext context) {
final DefaultTextStyle defaultTextStyle = DefaultTextStyle.of(context);
TextStyle effectiveTextStyle = style;
if (style == null || style.inherit)
effectiveTextStyle = defaultTextStyle.style.merge(style);
if (MediaQuery.boldTextOverride(context))
effectiveTextStyle = effectiveTextStyle.merge(const TextStyle(fontWeight: FontWeight.bold));
Widget result = RichText( ---->Text原來是經過RichText這個widget來構建樹
textAlign: textAlign ?? defaultTextStyle.textAlign ?? TextAlign.start,
textDirection: textDirection, // RichText uses Directionality.of to obtain a default if this is null.
locale: locale, // RichText uses Localizations.localeOf to obtain a default if this is null
softWrap: softWrap ?? defaultTextStyle.softWrap,
overflow: overflow ?? defaultTextStyle.overflow,
textScaleFactor: textScaleFactor ?? MediaQuery.textScaleFactorOf(context),
maxLines: maxLines ?? defaultTextStyle.maxLines,
strutStyle: strutStyle,
text: TextSpan(
style: effectiveTextStyle,
text: data,
children: textSpan != null ? <TextSpan>[textSpan] : null,
),
);
....
複製代碼
繼續往RichText
裏面看:
....
@override
RenderParagraph createRenderObject(BuildContext context) {
assert(textDirection != null || debugCheckHasDirectionality(context));
//返回RenderParagraph widget
return RenderParagraph(text,
textAlign: textAlign,
textDirection: textDirection ?? Directionality.of(context),
softWrap: softWrap,
overflow: overflow,
textScaleFactor: textScaleFactor,
maxLines: maxLines,
strutStyle: strutStyle,
locale: locale ?? Localizations.localeOf(context, nullOk: true),
);
}
....
複製代碼
發現最終它會返回RenderParagraph
widget,繼續往裏看:
class RichText extends LeafRenderObjectWidget {
}
複製代碼
能夠發現RichText
繼承LeafRenderObjectWidget
,繼續往下看:
//用於配置RenderObject子類的RenderObjectWidgets的超類,沒有孩子,也就是沒有字節點(child)
abstract class LeafRenderObjectWidget extends RenderObjectWidget {
const LeafRenderObjectWidget({ Key key }) : super(key: key);
//構建出類型是LeafRenderObjectElement
@override
LeafRenderObjectElement createElement() => LeafRenderObjectElement(this);
}
複製代碼
能夠看到LeafRenderObjectWidget
是抽象類,而且繼承了RenderObjectWidget
,繼續往下看:
/// RenderObjectWidgets provide the configuration for [RenderObjectElement]s,
/// which wrap [RenderObject]s, which provide the actual rendering of the
/// application.
//上面註釋是:RenderObjectWidgets向[RenderObjectElement]提供了配置信息,包裝了[RenderObject],在應用程序了提供了實際的渲染
abstract class RenderObjectWidget extends Widget {
const RenderObjectWidget({ Key key }) : super(key: key);
//建立element
@override
RenderObjectElement createElement();
//建立RenderObject
@protected
RenderObject createRenderObject(BuildContext context);
//更新RenderObject
@protected
void updateRenderObject(BuildContext context, covariant RenderObject renderObject) { }
//卸載RenderObject
@protected
void didUnmountRenderObject(covariant RenderObject renderObject) { }
}
複製代碼
由註釋能夠知道RenderObject
纔是繪製UI背後的真正對象,那下面繼續簡單跟蹤:
先看看RenderObjectWidget
繼承關係:
RenderObjectWidgets
的超類,用於配置有單個孩子的
RenderObject
子類(爲子類提供存儲,實際不提供更新邏輯),並列了具體的實際
widget
,如經常使用的Align、ClipRect、DecoratedBox都是屬於這類。
RenderObjectWidgets
的超類,用於配置有單個
children(也就是多個child)的
RenderObject
子類,如Flex、Flow、Stack都屬於這類。
RenderObjectWidgets
的超類,用於配置沒有孩子的
RenderObject
子類,如:RichText(平時的Text)、RawImage、Texture等。 這裏總結一下:
SingleChildRenderObjectWidget
用做只有一個child的widget
。MultiChildRenderObjectWidget
用做有children(多個孩子)的widget
。LeafRenderObjectWidget
用做沒有child的widget
。RenderObjectWidget
定義了建立,更新,刪除RenderObject的方法,子類必須實現,RenderObject
是最終佈局、UI渲染的實際對象。那麼假如我如今界面上,假如佈局以下:
那麼渲染流程以下: 這時候會想:爲何要加中間層Element
呢,不直接由
Widget
直接轉換成
RendObject
不是直接更好麼?其實並非這樣的。首先知道Flutter是響應式框架,在某一個時刻,可能會受到不一樣的輸入流影響,中間層
Element
對這一時刻的事件作了彙總,最後將須要修改的部分同步到
RendObject
tree上,也就是:
RenderObject
tree的更改,提升頁面渲染效率。上面得出UI樹是由一個個element
節點組成,可是最終的渲染是經過RenderObject
來完成,一個widget
從建立到顯示到界面的流程是:widget生成element,而後經過createRenderObject方法建立對應的RenderObject
關聯到Element.renderObject
上,最後經過RenderObject
來完成繪製。
上面知道,widget
並非真正顯示的對象,知道了一個widget
是怎樣渲染在屏幕上顯示,那下面就從main()方法入口,看看一個app從點擊啓動到運行的流程:
//app入口
void main() => runApp(MyApp());
複製代碼
app入口函數就是調用了runApp方法,看看runApp方法裏面作了什麼:
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..attachRootWidget(app)
..scheduleWarmUpFrame();
}
複製代碼
首先參數(Widget app)是一個widget
,下面一行一行分析,進入**WidgetsFlutterBinding.ensureInitialized()**方法:
/// A concrete binding for applications based on the Widgets framework.
/// This is the glue that binds the framework to the Flutter engine.
//意思:基於widget framework的應用程序的具體綁定
//這是將framework widget和Flutter engine綁定的橋樑
class WidgetsFlutterBinding extends BindingBase with GestureBinding, ServicesBinding, SchedulerBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
/// Returns an instance of the [WidgetsBinding], creating and
/// initializing it if necessary. If one is created, it will be a
/// [WidgetsFlutterBinding]. If one was previously initialized, then
/// it will at least implement [WidgetsBinding].
///
/// You only need to call this method if you need the binding to be
/// initialized before calling [runApp].
///
/// In the `flutter_test` framework, [testWidgets] initializes the
/// binding instance to a [TestWidgetsFlutterBinding], not a
/// [WidgetsFlutterBinding].
//上面註釋意思是:返回[WidgetsBinding]的具體實例,必須建立和初始化,若是已//經建立了,那麼它就是一個[WidgetsFlutterBinding],若是已經初始化了,那麼//至少要實現[WidgetsBinding]
//若是你只想調用這個方法,那麼你須要在調用runApp以前綁定而且初始化
static WidgetsBinding ensureInitialized() {
if (WidgetsBinding.instance == null)
WidgetsFlutterBinding();
return WidgetsBinding.instance;
}
}
複製代碼
看到這個WidgetsFlutterBinding
混入(with)不少的Binding
,下面先看父類BindingBase
:
abstract class BindingBase {
BindingBase() {
developer.Timeline.startSync('Framework initialization');
assert(!_debugInitialized);
initInstances();
assert(_debugInitialized);
assert(!_debugServiceExtensionsRegistered);
initServiceExtensions();
assert(_debugServiceExtensionsRegistered);
developer.postEvent('Flutter.FrameworkInitialization', <String, String>{});
developer.Timeline.finishSync();
}
static bool _debugInitialized = false;
static bool _debugServiceExtensionsRegistered = false;
ui.Window get window => ui.window;//獲取window實例
@protected
@mustCallSuper
void initInstances() {
assert(!_debugInitialized);
assert(() { _debugInitialized = true; return true; }());
}
}
複製代碼
看到有句代碼ui.Window get window => ui.window;,在Android中全部的視圖都是經過window
來呈現的,那Flutter中也有window
,那看看window
在Flutter中的做用看看官方對它的定義:
class Window {
Window._();
//返回DPI,DPI是每英寸的像素點數,是設備屏幕的固件屬性
//獲取可能不許確
double get devicePixelRatio => _devicePixelRatio;
double _devicePixelRatio = 1.0;
//繪製UI的區域大小
Size get physicalSize => _physicalSize;
Size _physicalSize = Size.zero;
//獲取矩形的物理像素
WindowPadding get viewInsets => _viewInsets;
WindowPadding _viewInsets = WindowPadding.zero;
//獲取內邊距
WindowPadding get padding => _padding;
WindowPadding _padding = WindowPadding.zero;
//當繪製區域改變時觸發
VoidCallback get onMetricsChanged => _onMetricsChanged;
VoidCallback _onMetricsChanged;
Zone _onMetricsChangedZone;
set onMetricsChanged(VoidCallback callback) {
_onMetricsChanged = callback;
_onMetricsChangedZone = Zone.current;
}
//當系統語言發生變化時觸發回調
Locale get locale {
if (_locales != null && _locales.isNotEmpty) {
return _locales.first;
}
return null;
}
//獲取系統語言
List<Locale> get locales => _locales;
List<Locale> _locales;
//當Local發生改變時觸發回調
VoidCallback get onLocaleChanged => _onLocaleChanged;
VoidCallback _onLocaleChanged;
Zone _onLocaleChangedZone;
set onLocaleChanged(VoidCallback callback) {
_onLocaleChanged = callback;
_onLocaleChangedZone = Zone.current;
}
//當系統字體大小改變時觸發回調
VoidCallback get onTextScaleFactorChanged => _onTextScaleFactorChanged;
VoidCallback _onTextScaleFactorChanged;
Zone _onTextScaleFactorChangedZone;
set onTextScaleFactorChanged(VoidCallback callback) {
_onTextScaleFactorChanged = callback;
_onTextScaleFactorChangedZone = Zone.current;
}
//屏幕亮度改變時觸發回調
VoidCallback get onPlatformBrightnessChanged => _onPlatformBrightnessChanged;
VoidCallback _onPlatformBrightnessChanged;
Zone _onPlatformBrightnessChangedZone;
set onPlatformBrightnessChanged(VoidCallback callback) {
_onPlatformBrightnessChanged = callback;
_onPlatformBrightnessChangedZone = Zone.current;
}
//屏幕刷新時會回調
FrameCallback get onBeginFrame => _onBeginFrame;
FrameCallback _onBeginFrame;
Zone _onBeginFrameZone;
set onBeginFrame(FrameCallback callback) {
_onBeginFrame = callback;
_onBeginFrameZone = Zone.current;
}
//繪製屏幕時回調
VoidCallback get onDrawFrame => _onDrawFrame;
VoidCallback _onDrawFrame;
Zone _onDrawFrameZone;
set onDrawFrame(VoidCallback callback) {
_onDrawFrame = callback;
_onDrawFrameZone = Zone.current;
}
//點擊或者指針事件觸發回調
PointerDataPacketCallback get onPointerDataPacket => _onPointerDataPacket;
PointerDataPacketCallback _onPointerDataPacket;
Zone _onPointerDataPacketZone;
set onPointerDataPacket(PointerDataPacketCallback callback) {
_onPointerDataPacket = callback;
_onPointerDataPacketZone = Zone.current;
}
//獲取請求的默認路由
String get defaultRouteName => _defaultRouteName();
String _defaultRouteName() native 'Window_defaultRouteName';
//該方法被調用後,onBeginFrame和onDrawFrame將緊連着會在合適時機調用
void scheduleFrame() native 'Window_scheduleFrame';
//更新應用在GPU上的渲染,這方法會直接調用Flutter engine的Window_render方法
void render(Scene scene) native 'Window_render';
//窗口的語義內容是否改變
bool get semanticsEnabled => _semanticsEnabled;
bool _semanticsEnabled = false;
//當窗口語言發生改變時回調
VoidCallback get onSemanticsEnabledChanged => _onSemanticsEnabledChanged;
VoidCallback _onSemanticsEnabledChanged;
Zone _onSemanticsEnabledChangedZone;
set onSemanticsEnabledChanged(VoidCallback callback) {
_onSemanticsEnabledChanged = callback;
_onSemanticsEnabledChangedZone = Zone.current;
}
//當用戶表達寫的動做時回調
SemanticsActionCallback get onSemanticsAction => _onSemanticsAction;
SemanticsActionCallback _onSemanticsAction;
Zone _onSemanticsActionZone;
set onSemanticsAction(SemanticsActionCallback callback) {
_onSemanticsAction = callback;
_onSemanticsActionZone = Zone.current;
}
//啓用其餘輔助功能回調
VoidCallback get onAccessibilityFeaturesChanged => _onAccessibilityFeaturesChanged;
VoidCallback _onAccessibilityFeaturesChanged;
Zone _onAccessibilityFlagsChangedZone;
set onAccessibilityFeaturesChanged(VoidCallback callback) {
_onAccessibilityFeaturesChanged = callback;
_onAccessibilityFlagsChangedZone = Zone.current;
}
//更新此窗口的語義數據
void updateSemantics(SemanticsUpdate update) native 'Window_updateSemantics';
//設置Isolate調試名稱
void setIsolateDebugName(String name) native 'Window_setIsolateDebugName';
//向特定平臺發送消息
void sendPlatformMessage(String name, ByteData data, PlatformMessageResponseCallback callback) {
final String error =
_sendPlatformMessage(name, _zonedPlatformMessageResponseCallback(callback), data);
if (error != null)
throw new Exception(error);
}
String _sendPlatformMessage(String name, PlatformMessageResponseCallback callback, ByteData data) native 'Window_sendPlatformMessage';
//獲取平臺消息的回調
PlatformMessageCallback get onPlatformMessage => _onPlatformMessage;
PlatformMessageCallback _onPlatformMessage;
Zone _onPlatformMessageZone;
set onPlatformMessage(PlatformMessageCallback callback) {
_onPlatformMessage = callback;
_onPlatformMessageZone = Zone.current;
}
//由_dispatchPlatformMessage調用
void _respondToPlatformMessage(int responseId, ByteData data) native 'Window_respondToPlatformMessage';
//平臺信息響應回調
static PlatformMessageResponseCallback _zonedPlatformMessageResponseCallback(PlatformMessageResponseCallback callback) {
if (callback == null)
return null;
// Store the zone in which the callback is being registered.
final Zone registrationZone = Zone.current;
return (ByteData data) {
registrationZone.runUnaryGuarded(callback, data);
};
}
}
複製代碼
能夠知道window
包含當前設備系統的一些信息和回調,那麼如今看看混入的各類Binding
:
onPointerDataPacket
回調。onPlatformMessage
回調。onBeginFrame
和onDrawFrame
回調。onMetricsChanged
和onTextScaleFactorChanged
回調。onLocaleChanged
,onBuildSchedule
回調。也就是WidgetsFlutterBinding.ensureInitialized()
這行代碼看名字是將WidgetsFlutterBinding
實例初始化。其實並不是那麼簡單,另外獲取一些系統基本信息和初始化監聽window
對象的一些事件,而後將這些事件按照上層Framework模型規則進行包裝、抽象最後分發。簡而言之WidgetsFlutterBinding
是Flutter engine
和Framework
的橋樑。
第二行是attachRootWidget(app)
,看名字意思是將根RootWidget
掛載。點進去看:
//若是這個widget有必要建立,就將它附加到[renderViewElement]
void attachRootWidget(Widget rootWidget) {
_renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
container: renderView,
debugShortDescription: '[root]',
child: rootWidget,
).attachToRenderTree(buildOwner, renderViewElement);
}
複製代碼
renderView
是UI渲染樹的根節點:
// The render tree that's attached to the output surface.
//掛載在渲染樹 rootNode根節點
RenderView get renderView => _pipelineOwner.rootNode;
複製代碼
rootWidget
是外面所傳進來的根Widget
,這裏不用管它,下面看attachToRenderTree(buildOwner, renderViewElement)
這個方法,根據意思,這個方法應該構建RenderTree
,這個方法須要兩個參數,首先看buildOwner
這個參數:
/// The [BuildOwner] in charge of executing the build pipeline for the
/// widget tree rooted at this binding.
BuildOwner get buildOwner => _buildOwner;
final BuildOwner _buildOwner = BuildOwner();
複製代碼
看官網對它的定義:
意思是:是widget framework的管理類,用來跟蹤哪些widget須要重建,並處理widget樹的其餘任務,例如管理樹的非活動元素列表, 並在調試時在熱重載期間在必要時觸發「重組」命令,下面看另一個參數renderViewElement
,代碼註釋以下:
/// The [Element] that is at the root of the hierarchy (and which wraps the
/// [RenderView] object at the root of the rendering hierarchy).
///
/// This is initialized the first time [runApp] is called.
Element get renderViewElement => _renderViewElement;
Element _renderViewElement;
複製代碼
renderViewElement
是renderView
對應的Element
對象,由於renderView
是樹的根,因此renderViewElement
位於層次結構的根部,那下面點擊attachToRenderTree
的源碼看:
/// Inflate this widget and actually set the resulting [RenderObject] as the
/// child of [container].
///
/// If `element` is null, this function will create a new element. Otherwise,
/// the given element will have an update scheduled to switch to this widget.
///
/// Used by [runApp] to bootstrap applications.
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
沒有建立,那麼就調用createElement
建立根element
,element
和widget
進行關聯。接着調用element.assignOwner(owner)
,這個方法其實就是設置這個element
的跟蹤,最後調用owner.buildScope
這個方法,這個方法是肯定更新widget
的範圍。若是element
已經建立了,將根element
和關聯的widget
設爲新的,而且從新構建這個element
,爲了後面的複用。
runApp()方法最後一行執行scheduleWarmUpFrame
方法:
/// Schedule a frame to run as soon as possible, rather than waiting for
/// the engine to request a frame in response to a system "Vsync" signal.
///
/// This is used during application startup so that the first frame (which is
/// likely to be quite expensive) gets a few extra milliseconds to run.
///
/// Locks events dispatching until the scheduled frame has completed.
///
/// If a frame has already been scheduled with [scheduleFrame] or
/// [scheduleForcedFrame], this call may delay that frame.
///
/// If any scheduled frame has already begun or if another
/// [scheduleWarmUpFrame] was already called, this call will be ignored.
///
/// Prefer [scheduleFrame] to update the display in normal operation.
void scheduleWarmUpFrame() {
if (_warmUpFrame || schedulerPhase != SchedulerPhase.idle)
return;
_warmUpFrame = true;
Timeline.startSync('Warm-up frame');
final bool hadScheduledFrame = _hasScheduledFrame;
// We use timers here to ensure that microtasks flush in between.
Timer.run(() {
assert(_warmUpFrame);
handleBeginFrame(null);--->1
});
Timer.run(() {
assert(_warmUpFrame);
handleDrawFrame(); ---->2
// We call resetEpoch after this frame so that, in the hot reload case,
// the very next frame pretends to have occurred immediately after this
// warm-up frame. The warm-up frame's timestamp will typically be far in
// the past (the time of the last real frame), so if we didn't reset the
// epoch we would see a sudden jump from the old time in the warm-up frame
// to the new time in the "real" frame. The biggest problem with this is
// that implicit animations end up being triggered at the old time and
// then skipping every frame and finishing in the new time.
resetEpoch();
_warmUpFrame = false;
if (hadScheduledFrame)
scheduleFrame();
});
// Lock events so touch events etc don't insert themselves until the
// scheduled frame has finished.
lockEvents(() async {
await endOfFrame;
Timeline.finishSync();
});
}
複製代碼
首先這個方法在SchedulerBinding
裏,這兩個方法主要執行了handleBeginFrame
和handleDrawFrame
方法:
void handleBeginFrame(Duration rawTimeStamp) {
...
try {
// TRANSIENT FRAME CALLBACKS
Timeline.startSync('Animate', arguments: timelineWhitelistArguments);
_schedulerPhase = SchedulerPhase.transientCallbacks;
final Map<int, _FrameCallbackEntry> callbacks = _transientCallbacks;
_transientCallbacks = <int, _FrameCallbackEntry>{};
callbacks.forEach((int id, _FrameCallbackEntry callbackEntry) {
if (!_removedIds.contains(id))
_invokeFrameCallback(callbackEntry.callback, _currentFrameTimeStamp, callbackEntry.debugStack);
});
_removedIds.clear();
} finally {
_schedulerPhase = SchedulerPhase.midFrameMicrotasks;
}
}
複製代碼
能夠看到主要是對transientCallbacks
隊列操做,這個集合主要是放一些臨時回調,存放動畫回調。
void handleDrawFrame() {
assert(_schedulerPhase == SchedulerPhase.midFrameMicrotasks);
Timeline.finishSync(); // end the "Animate" phase
try {
// PERSISTENT FRAME CALLBACKS ----->
_schedulerPhase = SchedulerPhase.persistentCallbacks;
for (FrameCallback callback in _persistentCallbacks)
_invokeFrameCallback(callback, _currentFrameTimeStamp);
// POST-FRAME CALLBACKS ------>
_schedulerPhase = SchedulerPhase.postFrameCallbacks;
final List<FrameCallback> localPostFrameCallbacks =
List<FrameCallback>.from(_postFrameCallbacks);
_postFrameCallbacks.clear();
for (FrameCallback callback in localPostFrameCallbacks)
_invokeFrameCallback(callback, _currentFrameTimeStamp);
} finally {
_schedulerPhase = SchedulerPhase.idle;
Timeline.finishSync(); // end the Frame
profile(() {
_profileFrameStopwatch.stop();
_profileFramePostEvent();
});
assert(() {
if (debugPrintEndFrameBanner)
debugPrint('▀' * _debugBanner.length);
_debugBanner = null;
return true;
}());
_currentFrameTimeStamp = null;
}
}
複製代碼
能夠看到執行persistentCallbacks
隊列,這個隊列用於存放一些持久的回調,不能再此類回調中在請求新的繪製幀,持久回調一經註冊則不能移除。接着執行postFrameCallbacks
這個隊列在每一Frame
(一次繪製)結束時只會調用一次,調用後被系統移除。
也就是scheduleWarmUpFrame
這個方法安排幀儘快執行,當一次幀繪製結束以前不會響應各類事件,這樣保證繪製過程當中不觸發重繪。上面說過:
RendererBinding:是渲染樹和Flutter engine的橋樑,提供onMetricsChanged和onTextScaleFactorChanged回調
Flutter真正渲染和繪製是在這個綁定裏:
void initInstances() {
super.initInstances();
_instance = this;//初始化
_pipelineOwner = PipelineOwner(
onNeedVisualUpdate: ensureVisualUpdate,
onSemanticsOwnerCreated: _handleSemanticsOwnerCreated,
onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed,
);
//添加設置監聽
window
..onMetricsChanged = handleMetricsChanged
..onTextScaleFactorChanged = handleTextScaleFactorChanged
..onPlatformBrightnessChanged = handlePlatformBrightnessChanged
..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged
..onSemanticsAction = _handleSemanticsAction;
initRenderView();
_handleSemanticsEnabledChanged();
assert(renderView != null);
//添加persistentFrameCallback
addPersistentFrameCallback(_handlePersistentFrameCallback);
//建立觸摸管理
_mouseTracker = _createMouseTracker();
}
複製代碼
addPersistentFrameCallback
這個方法主要向persistentFrameCallback添加了回調:
void addPersistentFrameCallback(FrameCallback callback) {
_persistentCallbacks.add(callback);
}
複製代碼
再看_handlePersistentFrameCallback
這個回調作了什麼:
void _handlePersistentFrameCallback(Duration timeStamp) {
drawFrame();
}
複製代碼
@protected
void drawFrame() {
assert(renderView != null);
pipelineOwner.flushLayout();//更新佈局信息
pipelineOwner.flushCompositingBits();//在flushLayout只後調用,在flushPaint以前調用,更新RenderObject是否須要重繪
pipelineOwner.flushPaint();//更新繪製RenderObject
renderView.compositeFrame(); // 發送bit數據給GPU
pipelineOwner.flushSemantics(); // 發送語義數據給操做系統
}
複製代碼
下面一個一個方法走:
void flushLayout() {
profile(() {
Timeline.startSync('Layout', arguments: timelineWhitelistArguments);
});
assert(() {
_debugDoingLayout = true;
return true;
}());
try {
// TODO(ianh): assert that we're not allowing previously dirty nodes to redirty themselves
while (_nodesNeedingLayout.isNotEmpty) {
final List<RenderObject> dirtyNodes = _nodesNeedingLayout;
_nodesNeedingLayout = <RenderObject>[];
for (RenderObject node in dirtyNodes..sort((RenderObject a, RenderObject b) => a.depth - b.depth)) {
if (node._needsLayout && node.owner == this)
node._layoutWithoutResize();
}
}
} finally {
assert(() {
_debugDoingLayout = false;
return true;
}());
profile(() {
Timeline.finishSync();
});
}
}
複製代碼
看源碼得知首先獲取哪些標記爲髒
的RenderObject
的佈局信息,而後經過ode._layoutWithoutResize();
從新調整這些RenderObject
。
void flushCompositingBits() {
profile(() { Timeline.startSync('Compositing bits'); });
_nodesNeedingCompositingBitsUpdate.sort((RenderObject a, RenderObject b) => a.depth - b.depth);
for (RenderObject node in _nodesNeedingCompositingBitsUpdate) {
if (node._needsCompositingBitsUpdate && node.owner == this)
node._updateCompositingBits();
}
_nodesNeedingCompositingBitsUpdate.clear();
profile(() { Timeline.finishSync(); });
}
複製代碼
檢查RenderObject
是否須要重繪,而且經過node._updateCompositingBits();
更新_needsCompositing
這個屬性,若爲true
就要從新繪製,不然不須要。
void flushPaint() {
profile(() { Timeline.startSync('Paint', arguments: timelineWhitelistArguments); });
assert(() {
_debugDoingPaint = true;
return true;
}());
try {
final List<RenderObject> dirtyNodes = _nodesNeedingPaint;
_nodesNeedingPaint = <RenderObject>[];
// Sort the dirty nodes in reverse order (deepest first).
//方向遍歷這些標記過的node
for (RenderObject node in dirtyNodes..sort((RenderObject a, RenderObject b) => b.depth - a.depth)) {
assert(node._layer != null);
if (node._needsPaint && node.owner == this) {
if (node._layer.attached) {
//從新繪製
PaintingContext.repaintCompositedChild(node);
} else {
node._skippedPaintingOnLayer();
}
}
}
assert(_nodesNeedingPaint.isEmpty);
} finally {
assert(() {
_debugDoingPaint = false;
return true;
}());
profile(() { Timeline.finishSync(); });
}
}
複製代碼
這個方法經過反向遍歷(dirty
標記)取得須要重繪的RenderObject
,最後經過PaintingContext.repaintCompositedChild(node);
重繪。
void compositeFrame() {
Timeline.startSync('Compositing', arguments: timelineWhitelistArguments);
try {
//建立Scene對象
final ui.SceneBuilder builder = ui.SceneBuilder();
final ui.Scene scene = layer.buildScene(builder);
if (automaticSystemUiAdjustment)
_updateSystemChrome();
//使用render方法將Scene對象顯示在屏幕上
_window.render(scene);//調用flutter engine的渲染API
scene.dispose();
assert(() {
if (debugRepaintRainbowEnabled || debugRepaintTextRainbowEnabled)
debugCurrentRepaintColor = debugCurrentRepaintColor.withHue((debugCurrentRepaintColor.hue + 2.0) % 360.0);
return true;
}());
} finally {
Timeline.finishSync();
}
}
複製代碼
Scene
是用來保存渲染後的最終像素信息,這個方法將Canvas
畫好的Scene
對象傳給window.render()
方法,該方法會直接將Scene
信息發送給Flutter engine,最終Flutter engine將圖像畫在設備屏幕上,這樣整個繪製流程就算完了。
注意:RendererBinding
只是混入對象,最終混入到WidgetsBinding
,回到最開始來看:
class WidgetsFlutterBinding extends BindingBase with GestureBinding, ServicesBinding, SchedulerBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
/// Returns an instance of the [WidgetsBinding], creating and
/// initializing it if necessary. If one is created, it will be a
/// [WidgetsFlutterBinding]. If one was previously initialized, then
/// it will at least implement [WidgetsBinding].
///
/// You only need to call this method if you need the binding to be
/// initialized before calling [runApp].
///
/// In the `flutter_test` framework, [testWidgets] initializes the
/// binding instance to a [TestWidgetsFlutterBinding], not a
/// [WidgetsFlutterBinding].
static WidgetsBinding ensureInitialized() {
if (WidgetsBinding.instance == null)
WidgetsFlutterBinding();
return WidgetsBinding.instance;
}
}
複製代碼
因此應該WidgetsBinding
來重寫實現drawFrame
方法:
@override
void drawFrame() {
assert(!debugBuildingDirtyElements);
assert(() {
debugBuildingDirtyElements = true;
return true;
}());
try {
if (renderViewElement != null)
buildOwner.buildScope(renderViewElement);
super.drawFrame(); //調用Renderbinding的drawFrame方法
buildOwner.finalizeTree();
} finally {
assert(() {
debugBuildingDirtyElements = false;
return true;
}());
}
profile(() {
if (_needToReportFirstFrame && _reportFirstFrame) {
developer.Timeline.instantSync('Widgets completed first useful frame');
developer.postEvent('Flutter.FirstFrame', <String, dynamic>{});
_needToReportFirstFrame = false;
}
});
}
複製代碼
widget
的功能是向element
提供配置信息,每個widget
在Flutter裏是一份配置數據,而表明屏幕背後的元素是element
,而真正的佈局、渲染是經過RenderObject
來完成的,從建立到渲染的主要流程是:widget
信息生成element
,建立對應的RenderObject
關聯到Element.renderObject
屬性上,最後經過RenderObject
佈局和繪製。window
對象的事件,將這些事件處理包裝爲Framework模型進行分發,經過widget
建立element
樹,接着經過scheduleWarmUpFrame
進行渲染,接着經過Rendererbinding
進行佈局,繪製,最後經過調用ui.window.render(scene)
Scene信息發給Flutter engine,Flutter engine最後調用渲染API把圖像畫在屏幕上。資料參考:
若有錯誤,歡迎指出指正,謝謝~