楔子
雷思是一名程式設計師,年齡老大不小了,家裡急啊,老催他相親。可是雷思同學對相親不太上心,相親網站應付似的填了點東西,然後截個圖給他老孃發過去,說是已經在相親網站掛出去了。其實他壓根就沒當回事——程式設計師嘛,哪裡會缺物件,現在用了 Dart 語言,連 new 都不需要了。 這一天,雷思正在找 bug,剛好定位到一個物件的記憶體分配問題,正準備動手改呢,叮咚,手機彈了一個訊息:“雷思,你好,小芙給你傳送了一個見面邀請,是否接受?”雷思正準備劃掉訊息,瞥了一眼頭像,突然有點心動,點了個“接受”按鈕。於是,雷思開始了他的第一次網上相親之旅。
相親的準備
小芙十分注重個人形象,而且又十分善於掩藏自己的內心狀態。從表面看她和普通的女孩子一樣,但是內心戲卻很足。
class Xiaofu extends StatefulWidget {
Xiaofu({Key key}) : super(key: key) {
print('constructor: 小芙');
}
@override
_XiaofuState createState() => _XiaofuState();
}
class _XiaofuState extends State<Xiaofu> {
//這是小芙的內心戲,表面看不到
}
複製程式碼
相反,程式設計師雷思很簡單,標準的單純技術男。
//簡簡單單的程式設計師——雷思
class Leisi extends StatelessWidget {
Leisi({Key key}) : super(key: key) {
print('constructor: 雷思');
}
}
複製程式碼
約定好的相親時間還有三個小時,小芙就開始梳妝打扮起來,這雷思可是她從好多簡歷裡挑出來的,必須精心準備。她始終記得媽媽教導過——女人面對男人時得優雅一點。
@override
void initState() {
super.initState(); //媽媽教導過的話
print('initState:小芙花了2小時化妝');
}
複製程式碼
這個時候呢,雷思還在寫程式碼。
相親的開場白
相親時間到了,雷思秉承著從國外名著學到的紳士習慣,提前了15分鐘達到了約定的餐廳——這地點是相親網站給他們推薦的。餐廳看著挺不錯,估計消費不低。這肯定是相親網站的合作餐廳,估計能給他們返不少點——雷思的網際網路平臺思維下意識就上來了。而小芙呢,自然是要等雷思到了之後才會出現。
@override
void didUpdateWidget(oldWidget) {
super.didUpdateWidget(oldWidget);
print('didUpdateWidget:小芙出現了');
}
複製程式碼
“你好!不好意思,讓你久等了!”小芙面帶微笑優雅地揮手向雷思打招呼。 雷思沒感覺到她的不好意思,只是突然有些侷促,“你……你好!呃,沒事,我也才到一會。” 小芙心裡飄過三個字——沒經驗,當然,這些雷思看不出來。
相親的過程
見了面了,招呼也打了,開始點菜吧。 “你來點吧!”雷思繼續遵循他的紳士風度,女士優先嘛! “你來吧,我也不懂吃些什麼。”小芙自然禮貌性地推託一下。 “呃,好吧!你喜歡吃什麼?” “隨便吧,我對吃沒什麼特別講究。” “那我就隨便點了啊。”雷思沒有注意到小芙的表情表了,真的隨便點了兩個菜。
@override
Widget build(BuildContext context) {
print('build:雷思');
return Center(
child: Text('雷思隨便點了兩個菜'),
);
}
複製程式碼
@override
Widget build(BuildContext context) {
print('build:小芙');
return Center(
child: Column(children: [
Text('表情:$_face'),
TextButton(
onPressed: () {
setState(() {
_face = '失望';
print('小芙的表情變了');
});
},
child: Text('改變表情')),
]),
);
}
複製程式碼
接下來的過程就有點無聊了,結果自然是吃完各回各家。
結局
“相親也就這樣吧。”雷思沒多想,回家洗個澡,翻了會掘金的沸點——這個比相親有趣多了!
而小芙回到家,失望之情依然沒有消散。她想不明白怎麼就一時腦熱要決定和這個叫雷思的人相親,程式設計師真的像外界傳言的那樣無趣(簡單)!過了相當一會,才平復自己的懊惱的情緒。
@override
void deactivate() {
print('deactivate:小芙的情緒平復了');
super.deactivate();
}
複製程式碼
正當小芙準備將雷思從關注列表刪除時,她突然想明白了當時為什麼會決定和雷思相親,應該是這個名字和她喜歡的蕾絲
一樣的讀音吧。她點了一下“取消關注”,就這樣,雷思從她的關注列表中消失了。
@override
void dispose() {
print('dispose:小芙取消關注雷思');
super.dispose();
}
複製程式碼
後記
從雷思和小芙的相親故事可以看到,雷思(StatelessWidget)作為無狀態元件,真的很簡單。而小芙(StatefulWidget)作為有狀態元件,內心戲十足。我們以一個演示頁面為例。
class StateDemoPage extends StatelessWidget {
StateDemoPage({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('雷思和小芙的故事'),
),
body: Column(
children: [
Xiaofu(),
Leisi(),
],
),
);
}
}
複製程式碼
首次進入該頁面時,列印出來的內容如下,可以看到小芙多了2個步驟,分別是initState
和didChangeDependencies
。也就是有狀態元件會多經歷兩個步驟:
initState
:初始化狀態,在物件插入到元件樹後呼叫。而在元件的狀態建立和初始化狀態之間,框架會給狀態繫結一個BuildContext
。通常我們會在這裡請求網路或其他介面所需要的資料。didChangeDependencies
:當狀態依賴的物件改變的時候被呼叫(例如一個InheritedWidget
被改變時),當initState
完成後(即初始化完成後)會馬上呼叫該方法。該方法呼叫後會呼叫build
重新構建元件樹。因為每次狀態依賴改變時都會呼叫build
方法,因此通常不需要子類過載該方法,除非是有些負荷過重的任務(如單次的網路請求)不想每次build
時呼叫。
flutter: constructor: 小芙
flutter: constructor: 雷思
flutter: initState:小芙花了2小時化妝
flutter: didChangeDependencies:小芙
flutter: build:小芙
flutter: build:雷思
複製程式碼
之後就是 build
方法了,這個在無狀態和有狀態元件都有。在有狀態元件中,當初始化完成或呼叫setState
如小芙的表情改變)會進行呼叫。
對於無狀態元件,後面就沒有別的方法了,但是對於有狀態元件還存在四個方法:
reassemble
:重灌時呼叫,這個一般是在熱過載的時候(我們修改完程式碼,直接儲存後介面會隨之更新)。熱過載會重新構建元件,但不會初始化狀態。只是會在構建完成後呼叫didUpdateWidget
,告知元件更新完成。didUpdateWidget
:當元件配置更改時呼叫。父元件重建時會呼叫樹中的有狀態的子元件的該方法。同時會顯示一個新的同型別元件和 key。框架會更新元件的狀態指向新的元件,然後呼叫該方法,並把舊元件傳遞給該方法。該方法呼叫後總是會呼叫 build 方法。通常在該方法中完成元件移除的動作,例如動畫。
flutter: reassemble:小芙
flutter: constructor: 小芙
flutter: constructor: 雷思
flutter: didUpdateWidget:小芙出現了
flutter: build:小芙
flutter: build:雷思
flutter: deactivate:小芙的情緒恢復了
flutter: dispose:小芙將雷思從關注列表移除了
複製程式碼
deactivate
:元件從元件樹移除時會被呼叫,當然有時候元件被移除後可能被重新插入到元件的別的位置,這時候會呼叫build 方法重新構建元件樹。如果完全從元件樹移除,之後就會呼叫dispose
銷燬元件。在該方法中可以解除大部分物件的引用,從而釋放資源,直到dispose
呼叫後釋放所有資源。dispose
:當有狀態元件再也不會呼叫 build時呼叫該方法(通常是退出頁面),在這裡可以消除一些引用物件(如定時器,尚未結束的動畫)來釋放資源。下圖展示了有狀態元件的狀態切換過程。
stateDiagram-v2
[*] --> Constructor
Constructor --> createState
createState --> didChangeDependencies
didChangeDependencies --> build
build --> deactivate: 退出
build-->build: setState
build-->reassemble: 熱過載
reassemble-->重新構建(constructor)
重新構建(constructor)-->didUpdateWidget
didUpdateWidget-->build
deactivate --> dispose
dispose --> [*]
從有狀態和無狀態元件的對比來看,有狀態元件要維護的生命週期函式多好幾個,效能上自然會消耗更多資源,因此如果沒有必要,推薦儘量使用無狀態元件。接下來的篇章我們看看元件真正的渲染過程。
我是島上碼農,微信公眾號同名。這是Flutter 入門與實戰的專欄文章。
??:覺得有收穫請點個贊鼓勵一下!
?:收藏文章,方便回看哦!
?:評論交流,互相進步!