Flutter是什么
Google開源的,基于skia(開源,C/C++)繪圖引擎,支持OpenGL,metal,Vulcan,使用dart語言進(jìn)行編寫的UI框架。
dart
源碼
Flutter提供兩種編譯模式
JIT:腳本模式,通過dart虛擬機(jī),解析代碼,重新裝載代碼,在框架中對應(yīng)的方法是resemble,在resemble覆蓋下的情況,可使用熱重載的方式調(diào)試代碼,如果熱重載沒反應(yīng),考慮重新進(jìn)入并加載頁面or重新編譯并啟動項目。
AOP:靜態(tài)編譯,編譯出來的項目,性能接近原生。
目前項目架構(gòu),混編模式
Flutter模塊是獨(dú)立于已有的iOS,Android項目的子項目,實(shí)現(xiàn)業(yè)務(wù)隔離。
通信通過已經(jīng)寫好的channel模塊進(jìn)行,可以實(shí)現(xiàn)方法調(diào)用,數(shù)據(jù)傳遞。
iOS模塊:OC+swift代碼編寫的,支持Flutter<->原生通信的模塊。
Android模塊:Java代碼編寫的,支持Flutter<->原生通信的模塊。
Flutter模塊:業(yè)務(wù)代碼
在混編項目中,flutter是在原生項目中是一個什么樣的存在
因為需要獲取原生層提供的頁面生命周期,所以本質(zhì)上,咱Android中,一個綁定與某個activity的view,在iOS中是一個綁定與一VC的view。
由于是獨(dú)立于原生的UI框架,使用時包含多個重型對象,因此首次啟動會比較慢。
Flutter配置,演示
配置鏈接
AS:
VS:
flutter 框架介紹

Widget
描述了他們的視圖在給定其當(dāng)前配置和狀態(tài)時應(yīng)該看起來像什么,可以理解為配置,類比Android和前端,是xml和css。但是widget不止于此,還可以用來描述事件,如GestureDetectorwidget并不具有顯示效果,而是檢測由用戶做出的手勢。
widget 非常輕量,雖然關(guān)聯(lián)繪制對象,但是框架層有進(jìn)行優(yōu)化,避免了不必要的大型對象重建/銷毀,所以在操作的過程中,不需要擔(dān)心new一個widget像new一個view那樣耗費(fèi)性能。
Element
通常作為框架創(chuàng)建的實(shí)例對象,存在于element樹中,存放上下文,通過Element遍歷視圖樹,Element同時持有Widget和RenderObject。
Renderobject
根據(jù)Widget的布局屬性進(jìn)行l(wèi)ayout,paint Widget傳入的內(nèi)容。
Widget,Element,Renderobject關(guān)系圖

樹的更新規(guī)則
1、找到widget對應(yīng)的element節(jié)點(diǎn),設(shè)置element為dirty,觸發(fā)drawframe, drawframe會調(diào)用element的performRebuild()進(jìn)行樹重建
2、 widget.build() == null, deactive element.child,刪除子樹,流程結(jié)束
3、 element.child.widget == NULL, mount 的新子樹,流程結(jié)束
4、element.child.widget == widget.build() 無需重建,否則進(jìn)入流程5
5、Widget.canUpdate(element.child.widget, newWidget) == true,更新child的slot,element.child.update(newWidget)(如果child還有子節(jié)點(diǎn),則遞歸上面的流程進(jìn)行子樹更新),流程結(jié)束,否則轉(zhuǎn)6
6、Widget.canUpdate(element.child.widget, newWidget) != true(widget的classtype 或者 key 不相等),deactivew element.child,mount 新子樹
注意事項:
1、element.child.widget == widget.build(),不會觸發(fā)子樹的update,當(dāng)觸發(fā)update的時候,如果沒有生效,要注意widget是否使用舊widget,沒有new widget,導(dǎo)致update流程走到該widget就停止了
2、子樹的深度變化,會引起子樹重建,如果子樹是一個復(fù)雜度很高的樹,可以使用GlobalKey做為子樹widget的key。GlobalKey具有緩存功能
如何觸發(fā)樹更新
1、全局更新:調(diào)用runApp(rootWidget),一般flutter啟動時調(diào)用后不再會調(diào)用
2、局部子樹更新, 將該子樹做StatefullWidget的一個子widget,并創(chuàng)建對應(yīng)的State類實(shí)例,通過調(diào)用state.setState() 觸發(fā)該子樹的刷新
幾個關(guān)鍵的源碼文件
framework.dart:介紹Flutter 作為一個UI框架,他的UI是怎么構(gòu)建的。
basic.dart:基礎(chǔ)控件庫,及一些基于基礎(chǔ)控件構(gòu)建的控件及相關(guān)示例。
object.dart:核心對象renderobject,包含頁面布局及頁面繪制前期工作等內(nèi)容。
入門開發(fā)演示
項目結(jié)構(gòu)
項目入口
頁面構(gòu)成
生命周期
1、initState(): state create之后被insert到tree時調(diào)用的
2、didUpdateWidget(newWidget):祖先節(jié)點(diǎn)rebuild widget時調(diào)用
3、deactivate():widget被remove的時候調(diào)用,一個widget從tree中remove掉,可以在dispose接口被調(diào)用前,重新instert到一個新tree中
4、didChangeDependencies():
- 初始化時,在initState()之后立刻調(diào)用
- 當(dāng)依賴的InheritedWidget rebuild,會觸發(fā)此接口被調(diào)用
build方法
- After calling [initState].
- After calling [didUpdateWidget].
- After receiving a call to [setState].
- After a dependency of this [State] object changes (e.g., an[InheritedWidget] referenced by the previous [build] changes).
- After calling [deactivate] and then reinserting the [State] object into the tree at another location.
dispose():Widget徹底銷毀時調(diào)用
reassemble(): hot reload調(diào)用
注意事項:
1、A頁面push一個新的頁面B,A頁面的widget樹中的所有state會依次調(diào)用deactivate(), didUpdateWidget(newWidget)、build()(這里懷疑是bug,A頁面push一個新頁面,理論上并沒有將A頁面進(jìn)行remove操作),當(dāng)然從功能上,沒有看出來有什么異常
2、當(dāng)ListView中的item滾動出可顯示區(qū)域的時候,item會被從樹中remove掉,此item子樹中所有的state都會被dispose,state記錄的數(shù)據(jù)都會銷毀,item滾動回可顯示區(qū)域時,會重新創(chuàng)建全新的state、element、renderobject
3、使用hot reload功能時,要特別注意state實(shí)例是沒有重新創(chuàng)建的,如果該state中存在一下復(fù)雜的資源更新需要重新加載才能生效,那么需要在reassemble()添加處理,不然當(dāng)你使用hot reload時候可能會出現(xiàn)一些意想不到的結(jié)果,例如,要將顯示本地文件的內(nèi)容到屏幕上,當(dāng)你開發(fā)過程中,替換了文件中的內(nèi)容,但是hot reload沒有觸發(fā)重新讀取文件內(nèi)容,頁面顯示還是原來的舊內(nèi)容。
路由
Route和 Navigator。 一個route是一個屏幕或頁面的抽象,Navigator是管理route的Widget。Navigator可以通過route入棧和出棧來實(shí)現(xiàn)頁面之間的跳轉(zhuǎn)。
InheritedWidget
數(shù)據(jù)從根往下傳數(shù)據(jù),常規(guī)做法是一層層往下,當(dāng)深度變大,數(shù)據(jù)的傳輸變的困難,flutter提供InheritedWidget用于子節(jié)點(diǎn)向祖先節(jié)點(diǎn)獲取數(shù)據(jù)的機(jī)制,使用方法如下:
class FrogColor extends InheritedWidget {
const FrogColor({
Key key,
@required this.color,
@required Widget child,
}) : assert(color != null),
assert(child != null),
super(key: key, child: child);
final Color color;
static FrogColor of(BuildContext context) {
return context.inheritFromWidgetOfExactType(FrogColor);
}
@override
bool updateShouldNotify(FrogColor old) => color != old.color;
}
child及其以下的節(jié)點(diǎn)可以通過調(diào)用下面的接口讀取color數(shù)據(jù)
FrogColor.of(context).color
context.inheritFromWidgetOfExactType(FrogColor)其實(shí)是通過context/element往上遍歷樹,查找到第一個FrogColor的祖先節(jié)點(diǎn),取該節(jié)點(diǎn)的widget對象
Notification
類似我們原生的通知方式,
1、注冊監(jiān)聽
2、dispatch監(jiān)聽信息
3、remove監(jiān)聽
Notification參考
用InheritedWidget實(shí)現(xiàn)自上而下的數(shù)據(jù)流動
用Notification實(shí)現(xiàn)數(shù)據(jù)從下往上的流動