谷歌大會以後,有很多人諮詢了我 Flutter 相關的問題,其中有很多是和麪試相關的,現在一些招聘上也開始羅列 Flutter 相關要求,最後想了想仍是寫一期總結吧,也算是 Flutter 的階段複習。java
⚠️系統完整的學習是必須須要的,這裏只能幫你總結一些知識點,更多的還請查閱 Dart/Flutter 官網。 android
本篇主要是知識點總結,若有疑問可點擊各文章連接瞭解詳情,或者查閱我 掘金專欄。git
其實學習過 JavaScript
或者 Java/Kotlin
的人,在學習 Dart
上幾乎是沒什麼難度的,Dart 綜合了動態語言和靜態語言的特性, 這裏主要提供一些不同,或者有意思的概念。github
一、Dart
屬因而強類型語言 ,但能夠用 var
來聲明變量,Dart
會自推導出數據類型,var
其實是編譯期的「語法糖」。dynamic
表示動態類型, 被編譯後,實際是一個 object
類型,在編譯期間不進行任何的類型檢查,而是在運行期進行類型檢查。面試
二、Dart
中 if
等語句只支持 bool
類型,switch
支持 String 類型。json
三、Dart
中數組和 List
是同樣的。redux
四、Dart
中,Runes
表明符號文字 , 是 UTF-32 編碼的字符串, 用於如 Runes input = new Runes('\u{1f596} \u{1f44d}');
數組
五、Dart
支持閉包。安全
六、Dart
中 number 類型分爲 int 和 double ,沒有 float 類型。bash
七、Dart
中 級聯操做符 能夠方便配置邏輯,以下代碼:
event
..id = 1
..type = ""
..actor = "";
複製代碼
比較有意思的賦值操做符有:
AA ?? "999" ///表示若是 AA 爲空,返回999
AA ??= "999" ///表示若是 AA 爲空,給 AA 設置成 999
AA ~/999 ///AA 對於 999 整除
複製代碼
Dart
方法能夠設置 參數默認值 和 指定名稱 。
好比: getDetail(Sting userName, reposName, {branch = "master"}){}
方法,這裏 branch 不設置的話,默認是 「master」 。參數類型 能夠指定或者不指定。調用效果: getRepositoryDetailDao(「aaa", "bbbb", branch: "dev");
。
Dart
沒有關鍵詞 public
、private
等修飾符,_
下橫向直接表明 private
,可是有 @protected
註解 。
Dart
中的多構造方法,能夠經過命名方法實現。
默認構造方法只能有一個,而經過 Model.empty()
方法能夠建立一個空參數的類,其實方法名稱隨你喜歡,而變量初始化值時,只須要經過 this.name
在構造方法中指定便可:
class ModelA {
String name;
String tag;
//默認構造方法,賦值給name和tag
ModelA(this.name, this.tag);
//返回一個空的ModelA
ModelA.empty();
//返回一個設置了name的ModelA
ModelA.forName(this.name);
}
複製代碼
Dart
中全部的基礎類型、類等都繼承 Object
,默認值是 NULL
, 自帶 getter
和 setter
,而若是是 final
或者 const
的話,那麼它只有一個 getter
方法,Object
都支持 getter、setter 重寫:
@override
Size get preferredSize {
return Size.fromHeight(kTabHeight + indicatorWeight);
}
複製代碼
assert
只在檢查模式有效,在開發過程當中,assert(unicorn == null);
只有條件爲真才正常,不然直接拋出異常,通常用在開發過程當中,某些地方不該該出現什麼狀態的判斷。
operator
後對類進行 +/- 操做。class Vector {
final int x, y;
Vector(this.x, this.y);
Vector operator +(Vector v) => Vector(x + v.x, y + v.y);
Vector operator -(Vector v) => Vector(x - v.x, y - v.y);
···
}
void main() {
final v = Vector(2, 3);
final w = Vector(2, 2);
assert(v + w == Vector(4, 5));
assert(v - w == Vector(0, 1));
}
複製代碼
支持重載的操做符 :
Dart
中沒有接口,類均可以做爲接口,把某個類當作接口實現時,只須要使用 implements
,而後複寫父類方法便可。
Dart
中支持 mixins
,按照出現順序應該爲extends
、 mixins
、implements
。
Dart
中可經過 Zone
表示指定代碼執行的環境,相似一個沙盒概念,在 Flutter
中 C++ 運行 Dart
也是在 _runMainZoned
內執行 runZoned
方法啓動,而咱們也能夠經過 Zone
,在運行環境內捕獲全局異常等信息:
runZoned(() {
runApp(FlutterReduxApp());
}, onError: (Object obj, StackTrace stack) {
print(obj);
print(stack);
});
複製代碼
同時你能夠給 runZoned
註冊方法,在須要時執行回調,以下代碼所示,這樣的在一個 Zone
內任何地方,只要能獲取 onData
這個 ZoneUnaryCallback
,就均可以調用到 handleData
///最終須要處理的地方
handleData(result) {
print("VVVVVVVVVVVVVVVVVVVVVVVVVVV");
print(result);
}
///返回獲得一個 ZoneUnaryCallback
var onData = Zone.current.registerUnaryCallback<dynamic, int>(handleData);
///執行 ZoneUnaryCallback 返回數據
Zone.current.runUnary(onData, 2);
複製代碼
異步邏輯能夠經過 scheduleMicrotask
能夠插入異步執行方法:
Zone.current.scheduleMicrotask((){
//todo something
});
複製代碼
更多可參看 :《Flutter完整開發實戰詳解(11、全面深刻理解Stream)》
Future
簡單了說就是對 Zone
的封裝使用。
好比 Future.microtask
中主要是執行了 Zone
的 scheduleMicrotask
,而 result._complete
最後調用的是 _zone.runUnary
等等。
factory Future.microtask(FutureOr<T> computation()) {
_Future<T> result = new _Future<T>();
scheduleMicrotask(() {
try {
result._complete(computation());
} catch (e, s) {
_completeWithErrorCallback(result, e, s);
}
});
return result;
}
複製代碼
Dart
中可經過 async
/await
或者 Future
定義異步操做,而事實上 async
/await
也只是語法糖,最終仍是經過編譯器轉爲 Future
。
有興趣看這裏 :
Stream
也是有對Zone
的另一種封裝使用。
Dart 中另一種異步操做, async*
/ yield
或者 Stream
可定義 Stream
異步, async*
/ yield
也只是語法糖,最終仍是經過編譯器轉爲 Stream
。 Stream 還支持同步操做。
1)、Stream
中主要有 Stream
、 StreamController
、StreamSink
和 StreamSubscription
四個關鍵對象,大體能夠總結爲:
StreamController
:如類名描述,用於整個 Stream
過程的控制,提供各種接口用於建立各類事件流。
StreamSink
:通常做爲事件的入口,提供如 add
, addStream
等。
Stream
:事件源自己,通常可用於監聽事件或者對事件進行轉換,如 listen
、where
。
StreamSubscription
:事件訂閱後的對象,表面上用於管理訂閱過等各種操做,如 cacenl
、pause
,同時在內部也是事件的中轉關鍵。
2)、通常經過 StreamController
建立 Stream
;經過 StreamSink
添加事件;經過 Stream
監聽事件;經過 StreamSubscription
管理訂閱。
3)、Stream
中支持各類變化,好比map
、expand
、where
、take
等操做,同時支持轉換爲 Future
。
更多可參看 :《Flutter完整開發實戰詳解(11、全面深刻理解Stream)》
Flutter 和 React Native 不一樣主要在於 Flutter UI是直接經過 skia 渲染的 ,而 React Native 是將 js 中的控件轉化爲原生控件,經過原生去渲染的 ,相關更多可查看:《移動端跨平臺開發的深度解析》。
Flutter 中存在 Widget
、 Element
、RenderObject
、Layer
四棵樹,其中 Widget
與 Element
是一對多的關係 ,
Element
中持有 Widget
和 RenderObject
, 而 Element
與 RenderObject
是一一對應的關係(除去 Element
不存在 RenderObject
的狀況,如 ComponentElement
是不具有 RenderObject
) ,
當 RenderObject
的 isRepaintBoundary
爲 true
時,那麼個區域造成一個 Layer
,因此不是每一個 RenderObject
都具備 Layer
的,由於這受 isRepaintBoundary
的影響。
更多相關可查閱 《Flutter完整開發實戰詳解(9、 深刻繪製原理)》
Flutter 中 Widget
不可變,每次保持在一幀,若是發生改變是經過 State
實現跨幀狀態保存,而真實完成佈局和繪製數組的是 RenderObject
, Element
充當二者的橋樑, State
就是保存在 Element
中。
Flutter 中的 BuildContext
只是接口,而 Element
實現了它。
Flutter 中 setState
實際上是調用了 markNeedsBuild
,該方法內部標記此Element
爲 Dirty
,而後在下一幀 WidgetsBinding.drawFrame
纔會被繪製,這能夠看出 setState
並非當即生效的。
Flutter 中 RenderObject
在 attch
/layout
以後會經過 markNeedsPaint();
使得頁面重繪,流程大概以下:
經過isRepaintBoundary 往上肯定了更新區域,經過 requestVisualUpdate 方法觸發更新往下繪製。
RenderObject
的佈局相關方法調用順序是 : layout
-> performResize
-> performLayout
-> markNeedsPaint
, 可是用戶通常不會直接調用 layout
,而是經過 markNeedsLayout
,具體流程以下:Flutter 中通常 json 數據從 String
轉爲 Object
的過程當中都須要先通過 Map
類型。
Flutter 中 InheritedWidget
通常用於狀態共享,如Theme
、Localizations
、 MediaQuery
等,都是經過它實現共享狀態,這樣咱們能夠經過 context
去獲取共享的狀態,好比 ThemeData theme = Theme.of(context);
在
Element
的inheritFromWidgetOfExactType
方法實現裏,有一個Map<Type, InheritedElement> _inheritedWidgets
的對象。
_inheritedWidgets
通常狀況下是空的,只有當父控件是InheritedWidget
或者自己是InheritedWidgets
時纔會有被初始化,而當父控件是InheritedWidget
時,這個Map
會被一級一級往下傳遞與合併 。因此當咱們經過
context
調用inheritFromWidgetOfExactType
時,就能夠往上查找到父控件的Widget
。
runtimeType
和 key
判斷更新:static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType
&& oldWidget.key == newWidget.key;
}
}
複製代碼
initState()
表示當前 State
將和一個 BuildContext
產生關聯,可是此時BuildContext
沒有徹底裝載完成,若是你須要在該方法中獲取 BuildContext
,能夠 new Future.delayed(const Duration(seconds: 0, (){//context});
一下。
didChangeDependencies()
在 initState()
以後調用,當 State
對象的依賴關係發生變化時,該方法被調用,初始化時也會調用。
deactivate()
當 State
被暫時從視圖樹中移除時,會調用這個方法,同時頁面切換時,也會調用。
dispose()
Widget 銷燬了,在調用這個方法以前,總會先調用 deactivate()。
didUpdateWidge
當 widget
狀態發生變化時,會調用。
經過 StreamBuilder
和 FutureBuilder
咱們能夠快速使用 Stream
和 Future
快速構建咱們的異步控件: 《Flutter完整開發實戰詳解(11、全面深刻理解Stream)》
Flutter 中 runApp
啓動入口實際上是一個 WidgetsFlutterBinding
,它主要是經過 BindingBase
的子類 GestureBinding
、ServicesBinding
、 SchedulerBinding
、PaintingBinding
、SemanticsBinding
、 RendererBinding
、WidgetsBinding
等,經過 mixins
的組合而成的。
Flutter 中的 Dart 的線程是以事件循環和消息隊列的形式存在,包含兩個任務隊列,一個是 microtask 內部隊列,一個是 event 外部隊列,而 microtask 的優先級又高於 event 。
由於 microtask 的優先級又高於 event, 同時會阻塞event 隊列,因此若是 microtask 太多就可能會對觸摸、繪製等外部事件形成阻塞卡頓哦。
UI Runner
、GPU Runner
、IO Runner
, Platform Runner
(原生主線程) ,同時在 Flutter 中能夠經過 isolate
或者 compute
執行真正的跨線程異步操做。Flutter 中經過 PlatformView
能夠嵌套原生 View
到 Flutter
UI 中,這裏面實際上是使用了 Presentation
+ VirtualDisplay
+ Surface
等實現的,大體原理就是:
使用了相似副屏顯示的技術,VirtualDisplay
類表明一個虛擬顯示器,調用 DisplayManager
的 createVirtualDisplay()
方法,將虛擬顯示器的內容渲染在一個 Surface
控件上,而後將 Surface
的 id 通知給 Dart,讓 engine 繪製時,在內存中找到對應的 Surface
畫面內存數據,而後繪製出來。em... 實時控件截圖渲染顯示技術。
Flutter 的 Debug 下是 JIT 模式,release下是AOT模式。
Flutter 中能夠經過 mixins AutomaticKeepAliveClientMixin
,而後重寫 wantKeepAlive
保持住頁面,記得在被保持住的頁面 build
中調用 super.build
。(由於 mixins 特性)。
Flutter 手勢事件主要是經過競技判斷的:
主要有 hitTest
把全部須要處理的控件對應的 RenderObject
, 從 child
到 parent
所有組合成列表,從最裏面一直添加到最外層。
而後從隊列頭的 child 開始 for 循環執行 handleEvent
方法,執行 handleEvent
的過程不會被攔截打斷。
通常狀況下 Down 事件不會決出勝利者,大部分時候是在 MOVE 或者 UP 的時候纔會決出勝利者。
競技場關閉時只有一個的就直接勝出響應,沒有勝利者就拿排在隊列第一個強制勝利響應。
同時還有 didExceedDeadline
處理按住時的 Down 事件額外處理,同時手勢處理通常在 GestureRecognizer
的子類進行。
Flutter 中 ListView
滑動其實都是經過改變 ViewPort
中的 child
佈局來實現顯示的。
經常使用狀態管理的:目前有 scope_model
、flutter_redux
、fish_redux
、bloc + Stream
等幾種模式,具體可見 : 《Flutter完整開發實戰詳解(12、全面深刻理解狀態管理設計)》
Flutter 中能夠經過 Platform Channel
讓 Dart 代碼和原生代碼通訊的:
BasicMessageChannel
:用於傳遞字符串和半結構化的信息。MethodChannel
:用於傳遞方法調用(method invocation)。EventChanne
l: 用於數據流(event streams)的通訊。
同時 Platform Channel
並不是是線程安全的 ,更多詳細可查閱閒魚技術的 《深刻理解Flutter Platform Channel》
其中基礎數據類型映射以下:
Android 中 Flutter
默認啓動時會在 FlutterActivityDelegate.java
中讀取 AndroidManifset.xml 內 meta-data
標籤,其中 io.flutter.app.android.SplashScreenUntilFirstFrame
標誌位若是爲 ture ,就會啓動 Splash 畫面效果(相似IOS的啓動頁面)。
啓動時原生代碼會讀取 android.R.attr.windowBackground
獲得指定的 Drawable
, 用於顯示啓動閃屏效果,以後而且經過 flutterView.addFirstFrameListener
,在onFirstFrame
中移除閃屏。
好了,暫時都這裏了,有問題修改會或則補充的,後面再加上。
《Flutter完整開發實戰詳解(1、Dart語言和Flutter基礎)》
《Flutter完整開發實戰詳解(4、Redux、主題、國際化)》
《Flutter完整開發實戰詳解(6、 深刻Widget原理)》
《Flutter完整開發實戰詳解(10、 深刻圖片加載流程)》
《Flutter完整開發實戰詳解(11、全面深刻理解Stream)》
《Flutter完整開發實戰詳解(12、全面深刻理解狀態管理設計)》