-
- 級(jí)聯(lián)操作符
Dart 中 級(jí)聯(lián)操作符 可以返回對(duì)象從而繼續(xù)執(zhí)行其他方法:
new Column( children: [ new Container(), ] ..addAll(List.generate(3, (index) { return Container(); })) ..addAll([ new HorizontalLine(height: 5), ]), );
-
- 一次性執(zhí)行匿名回調(diào)函數(shù)
Dart 支持一次性執(zhí)行匿名回調(diào)函數(shù)戈稿,如:
void test() { () { print('hi'); }(); }
也可在組件中運(yùn)用并接收參數(shù),如:
@override Widget build(BuildContext context) { return new Scaffold( body: new Text((String text) { return text; }('我是文字')), }
-
- 可選方法參數(shù)
Dart
方法可以設(shè)置 參數(shù)默認(rèn)值 和 指定名稱 讶舰。比如:
getDetail(Sting userName, reposName, {branch = "master"}){}
方法鞍盗,這里 branch 不設(shè)置的話,默認(rèn)是 “master” 跳昼。參數(shù)類型 可以指定或者不指定般甲。調(diào)用效果:getRepositoryDetailDao(“aaa", "bbbb", branch: "dev");
。 -
- Assert (斷言)
assert
只在檢查模式有效庐舟,在開(kāi)發(fā)過(guò)程中欣除,assert(unicorn == null);
只有條件為真才正常,否則直接拋出異常挪略,一般用在開(kāi)發(fā)過(guò)程中历帚,某些地方不應(yīng)該出現(xiàn)什么狀態(tài)的判斷。 -
- 擴(kuò)展
擴(kuò)展可以給指定類型的數(shù)值增加獲取方法杠娱,如( ScreenUtil 給 num 加的擴(kuò)展):
extension SizeExtension on num { num get w => ScreenUtil().setWidth(this); num get h => ScreenUtil().setHeight(this); num get sp => ScreenUtil().setSp(this); num get ssp => ScreenUtil().setSp(this, allowFontScalingSelf: true); }
比如我想獲取一個(gè) ScreenUtil 適配的50寬度值:
Container(width: 50.w)
等同于:
Container(width: ScreenUtil().setWidth(50))
從而簡(jiǎn)化了代碼挽牢,提升了開(kāi)發(fā)效率。
-
- runZoned
可以在自己的區(qū)域中運(yùn)行指定代碼(根據(jù) zoneSpecification 使用 Zone.fork 創(chuàng)建的區(qū)域)摊求,如果代碼出現(xiàn)錯(cuò)誤將拋出全局錯(cuò)誤在 onError:
runZoned(() { runApp(MyApp()); }, onError: (Object obj, StackTrace stack) { print(obj); print(stack); });
我們通用的錯(cuò)誤信息統(tǒng)計(jì)平臺(tái)如 sentry禽拔,一般都是放在 runZoned 的 onError 內(nèi)。
組件
1. ListView 自動(dòng)內(nèi)邊距
ListView 會(huì)在腳手架中未寫(xiě) AppBar 的情況下自動(dòng)添加 padding 內(nèi)邊距室叉,內(nèi)邊距值是狀態(tài)欄高度
2. Draggable 重繪問(wèn)題
Draggable 的 child 和 feedback 是同一個(gè)組件是每次拖動(dòng)都會(huì)重繪組件睹栖,如果不想被重繪,在組件的 key 給個(gè) GlobalKey即可茧痕。
3. 拿到數(shù)組模型索引
List.generate 可以獲得數(shù)組模型的索引 index野来,length 寫(xiě)數(shù)組模型的長(zhǎng)度即可。
4. 輸入框內(nèi)容被清空
如果出現(xiàn)輸入框內(nèi)容輸入的時(shí)候突然返回到桌面踪旷,回來(lái)的時(shí)候被清空了曼氛,可以嘗試使用 WidgetsBindingObserver 監(jiān)聽(tīng)不可見(jiàn)生命周期,輸入框焦點(diǎn)自動(dòng)取消令野,從而輸入框會(huì)保存原有內(nèi)容舀患。(這個(gè)問(wèn)題在早期 flutter 版本出現(xiàn))
5. 刷新頁(yè)面的指定組件
如果只想刷新某個(gè)頁(yè)面的指定組件可使用 GlobalKey 包裹此組件的 State 類,調(diào)用此組件的時(shí)候傳過(guò)去气破,想刷新的時(shí)候就可以在外面調(diào)用這個(gè) GlobalKey 的 setState 或其他刷新方法了聊浅。
6. 拿特殊滑動(dòng)組件內(nèi)控制器
特殊滑動(dòng)組件如 NestedScrollView 是有內(nèi)外控制器的,普通的賦值 controller 只能拿到外部的滑動(dòng)值现使,想要拿到內(nèi)控制可以在 NestedScrollView 的 body 中類在上下文獲取低匙,調(diào)用上下文的 ancestorWidgetOfExactType
class DataBody extends StatefulWidget {
@override
_DataBodyState createState() => _DataBodyState();
}
class _DataBodyState extends State<DataBody> {
ScrollController pageScrollController;
Type typeOf<T>() => T;
@override
void initState() {
super.initState();
PrimaryScrollController primaryScrollController =
context.ancestorWidgetOfExactType(typeOf<PrimaryScrollController>());
pageScrollController = primaryScrollController.controller;
}
}
不過(guò)這個(gè)在 1.12 已被廢棄,新版 Flutter 使用 findAncestorWidgetOfExactType
代替朴下。
功能
1. 無(wú)需上下文路由
自己定義個(gè) NavigatorState 的全局 key 然后賦值到 MaterialApp 的 navigatorKey 就能使用 NavigatorState 的所有功能并無(wú)需上下文努咐。
2. Future 執(zhí)行完成錯(cuò)誤
如果出現(xiàn) Future 執(zhí)行完成錯(cuò)誤可嘗試使用 Completer 的 future,如:
Future<Null> _refreshData() {
final Completer<Null> completer = new Completer<Null>();
new Future.delayed(new Duration(seconds: 2), () {
completer.complete(null);
});
return completer.future;
}
3. 調(diào)用方法出現(xiàn) null 錯(cuò)誤
dispose 銷毀某對(duì)象時(shí)在調(diào)用 .dispose
前加個(gè) ?
問(wèn)號(hào)殴胧,可以避免前面的為空導(dǎo)致調(diào)用不到 dispose 方法報(bào)錯(cuò)渗稍。
4. 頁(yè)面狀態(tài)保存
Flutter 中可以通過(guò) mixins AutomaticKeepAliveClientMixin
,然后重寫(xiě) wantKeepAlive
保持住頁(yè)面团滥,記得在被保持住的頁(yè)面 build
中調(diào)用 super.build
竿屹。
5. 手勢(shì)識(shí)別范圍
使用 GestureDetector 范圍只有 child 的設(shè)定顏色或使用區(qū)域才可被觸發(fā),behavior 為 HitTestBehavior.translucent
時(shí)整個(gè) child 會(huì)被觸發(fā)灸姊,InkWell 組件默認(rèn)整個(gè) child 會(huì)被觸發(fā)拱燃。
hitTest
得到一個(gè)包含所有待處理控件列表 HitTestResult
dispatchEvent
進(jìn)行事件分發(fā)并產(chǎn)生競(jìng)爭(zhēng),最終勝利者可以得到事件響應(yīng)權(quán)
如果同一個(gè)區(qū)域內(nèi)有多個(gè)控件都實(shí)現(xiàn)了 handleEvent
力惯,那么最后事件應(yīng)該交給誰(shuí)處理呢碗誉?
事件競(jìng)爭(zhēng)只有符合以下兩個(gè)條件之一的召嘶,才可以得到事件的處理權(quán)。
- 最后得到直接勝利的控件
- 活到最后的控件中排在列表第一位的控件
6. Flutter 手勢(shì)事件主要是通過(guò)競(jìng)技判斷
主要有 hitTest
把所有需要處理的控件對(duì)應(yīng)的 RenderObject
哮缺, 從 child
到 parent
全部組合成列表弄跌,從最里面一直添加到最外層。
然后從隊(duì)列頭的 child 開(kāi)始 for 循環(huán)執(zhí)行 handleEvent
方法尝苇,執(zhí)行 handleEvent
的過(guò)程不會(huì)被攔截打斷铛只。
一般情況下 Down 事件不會(huì)決出勝利者,大部分時(shí)候是在 Move 或者 up 的時(shí)候才會(huì)決出勝利者糠溜。
競(jìng)技場(chǎng)關(guān)閉時(shí)只有一個(gè)的就直接勝出響應(yīng)淳玩,沒(méi)有勝利者就拿排在隊(duì)列第一個(gè)強(qiáng)制勝利響應(yīng)。
同時(shí)還有 didExceedDeadline
處理按住時(shí)的 Down 事件額外處理非竿,同時(shí)手勢(shì)處理一般在 GestureRecognizer
的子類進(jìn)行蜕着。
插件開(kāi)發(fā)
1. android 獲取 context 和 activity
可以在插件的 Plugin 類實(shí)現(xiàn) ActivityAware,然后在 onAttachedToActivity 和 onReattachedToActivityForConfigChanges 都可以拿到 ActivityPluginBinding 的 binding汽馋,binding 調(diào)用getActivity侮东。
public class DemoPlugin implements FlutterPlugin, MethodCallHandler, ActivityAware {
static Context ctx;
static Activity activity;
@Override
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
ctx = binding.getApplicationContext();
}
@Override
public void onAttachedToActivity(ActivityPluginBinding binding) {
activity = binding.getActivity();
ctx = binding.getActivity();
}
@Override
public void onDetachedFromActivityForConfigChanges() {
}
@Override
public void onReattachedToActivityForConfigChanges(ActivityPluginBinding binding) {
ctx = binding.getActivity();
activity = binding.getActivity();
}
}
2. PlatformView
Flutter 中通過(guò) PlatformView
可以嵌套原生 View
到 Flutter
UI 中,這里面其實(shí)是使用了 Presentation
+ VirtualDisplay
+ Surface
等實(shí)現(xiàn)的豹芯,大致原理就是:
使用了類似副屏顯示的技術(shù)悄雅,VirtualDisplay
類代表一個(gè)虛擬顯示器,調(diào)用 DisplayManager
的 createVirtualDisplay()
方法铁蹈,將虛擬顯示器的內(nèi)容渲染在一個(gè) Surface
控件上宽闲,然后將 Surface
的 id 通知給 Dart,讓 engine 繪制時(shí)握牧,在內(nèi)存中找到對(duì)應(yīng)的 Surface
畫(huà)面內(nèi)存數(shù)據(jù)容诬,然后繪制出來(lái)。實(shí)時(shí)控件截圖渲染顯示技術(shù)沿腰。
3. Platform Channel
Flutter 中可以通過(guò) Platform Channel
讓 Dart 代碼和原生代碼通信的:
BasicMessageChannel
:用于傳遞字符串和半結(jié)構(gòu)化的信息览徒。MethodChannel
:用于傳遞方法調(diào)用(method invocation)。EventChannel
: 用于數(shù)據(jù)流(event streams)的通信颂龙。
同時(shí) Platform Channel
并非是線程安全的
更多詳細(xì)可查閱閑魚(yú)技術(shù)的 《深入理解Flutter Platform Channel》
介紹
1. 運(yùn)行模式
Flutter 的 Debug 下是 JIT 模式习蓬,release 下是 AOT 模式。目前主流的語(yǔ)言大多數(shù)只支持其中一種編譯方式措嵌,如 C 僅支持 AOT躲叼,JavaScript 僅支持 JIT,一般來(lái)說(shuō)靜態(tài)語(yǔ)言使用 AOT企巢,動(dòng)態(tài)語(yǔ)言使用 JIT
AOT 編譯方式下枫慷,編譯器必須在執(zhí)行代碼前將代碼編譯成機(jī)器執(zhí)行的原生代碼,在程序運(yùn)行時(shí)就不需要做其他額外的操作直接快速執(zhí)行,但是編譯時(shí)需要區(qū)分用戶機(jī)器架構(gòu)或听,生成不同架構(gòu)的二進(jìn)制代碼
JIT 程序運(yùn)行前不需要編譯代碼探孝,而是在運(yùn)行時(shí)動(dòng)態(tài)編譯,不用考慮用戶機(jī)器是什么架構(gòu)神帅,雖然縮短了開(kāi)發(fā)周期再姑,但是可能導(dǎo)致程序執(zhí)行速度更慢