PS:你的計劃很完美,但世界變化是在太快。
上篇文章學習了 Flutter 中的圖片加載及源碼分析,做過移動端開發的朋友都知道組件的生命週期,Flutter 中也是一樣,了解和學習好 Flutter 中的組件的生命週期非常重要,同系列文章如下:
本篇文章將主要介紹 Flutter 中 Widget 的生命週期,具體如下:
- StatelessWidget
- StatefulWidget
- State 生命週期狀態
- State 生命週期方法
StatelessWidget#
StatelessWidget 派生的組件是無狀態組件,無狀態組件只是在構建的時候渲染一次,不支持動態變化,即無法通過其他用戶操作重繪組件,只能接收傳入時的參數進行構建,如下 :
/// StatelessWidget
/// 表示無狀態Widget
class StatelessSamplePage extends StatelessWidget {
// 外部傳入數據
final String data;
StatelessSamplePage(this.data);
@override
Widget build(BuildContext context) {
return Container(
color: Colors.lightBlue,
child: Text(data),
);
}
}
如上傳入的參數只能使用 final 修飾,否則會出現如下警告:
This class (or a class which this class inherits from) is marked as '@immutable', but one or more of its instance fields are not final: StatelessSamplePage.data
提示 Widget 被 @immutable 註解修飾,如下:
@immutable
abstract class Widget extends DiagnosticableTree {
此時只能使用 final 來修飾變量,Dart 中被 final 修飾的變量只能被初始化一次,這也符合 StatelessWidget 的無狀態特徵。
StatefulWidget#
StatefulWidget 派生的組件是有狀態組件,有狀態的組件是支持隨著數據變化多次構建 Widget 以完成動態界面的渲染,如果要實現一個實時顯示當前時間的界面,顯然 StatelessWidget 是不能完成的,只能使用有狀態的 StatefulWidget 來實現,如下:
/// StatefulWidget
/// 表示有狀態的 Widget
class StatefulSamplePage extends StatefulWidget {
@override
_StatefulSamplePageState createState() => _StatefulSamplePageState();
}
class _StatefulSamplePageState extends State<StatefulSamplePage> {
DateFormat _dateFormat;
String _currentTime;
Timer _timer;
@override
void initState() {
super.initState();
_dateFormat = new DateFormat("yyyy-MM-dd HH:mm:ss");
// 初始化當前時間
_currentTime = _dateFormat.format(DateTime.now());
// 更新時間
_startTime();
}
@override
Widget build(BuildContext context) {
return Center(
child: Scaffold(
appBar: AppBar(
title: Text("Stateful Widget sample"),
centerTitle: true,
),
body: Align(
alignment: Alignment.topCenter,
child: Text("當前時間:$_currentTime"),
),
),
);
}
@override
void dispose() {
super.dispose();
if(_timer!=null){
_timer.cancel();
}
}
_startTime() {
const Duration duration = Duration(seconds: 1);
_timer = Timer.periodic(
duration,
(Timer timer) => {
// 刷新界面狀態
setState(() {
_currentTime = _dateFormat.format(DateTime.now());
})
});
}
}
效果如下:
實際上,如果只是靜態界面 StatelessWidget 和 StatefulWidget 是完全沒有區別的,都是能夠實現的,唯一区別就是 StatefulWidget 可以通過 setState 方法觸發 Widget 的重新構建, State 類就是就是 Stateless -> Stateful 的橋樑。
State 生命週期狀態#
Flutter 生命週期實際上各個組件的生命週期:
- StatelessWidget:無狀態組件的生命週期只有 build 構建這個過程;
- StatefulWidget:無狀態組件的生命週期指的是 State 的生命週期。
Flutter 生命週期實際上就是無狀態組件的生命週期,即 State 的生命週期,如下圖所示:
如上各個 State 的生命週期狀態主要是三個:
- created:指的是 State 的創建狀態,當調用 createState 方法之後就處於 created state;
- dirty:指的是當調用 setState 等方法數據發生變化,但是 Widget 還未重新構建時的狀態;
- clean:指的是 Widget 構建後的狀態;
- defunct:指的是 State.dispose 調用之後的狀態,此時對應的 Widget 已被銷毀不能夠被再次構建。
State 生命週期方法#
有狀態組件的生命週期就是 State 的生命週期,其具體調用過程與 build 觸發時機如下圖所示:
其生命週期方法具體含義如下:
- createState:StatefulWidget 中用於創建 State;
- initState:State 的初始化操作,如變量的初始化等;
- didChangeDependencies:initState 調用之後調用,或者使用了 InheritedWidgets 組件會被調用,其中 InheritedWidgets 可用於 Flutter 狀態管理;
- build:用於 Widget 的構建;
- deactivate:包含此 State 對象的 Widget 被移除之後調用,若此 Widget 被移除之後未被添加到其他 Widget 樹結構中,則會繼續調用 dispose 方法;
- dispose:該方法調用後釋放 Widget 所佔資源;
- reassemble:用於開發階段,熱重載的時候會被調用,之後會重新構建;
- didUpdateWidget:父 Widget 構建的時候子 Widget 的 didUpdateWidget 方法會被調用。
在對應的 Widget 生命週期方法中加上日誌,驗證上述生命週期方法的執行,父 Widget 源碼如下:
const String TAG = "Flutter";
/// Widget生命週期
class WidgetLifecycleSamplePage extends StatefulWidget {
@override
_WidgetLifecycleSamplePageState createState() {
Log.info(TAG, "parent createState");
return _WidgetLifecycleSamplePageState();
}
}
class _WidgetLifecycleSamplePageState extends State<WidgetLifecycleSamplePage> {
num _count = 0;
@override
void initState() {
super.initState();
Log.info(TAG, "parent initState");
}
@override
Widget build(BuildContext context) {
Log.info(TAG, "parent build");
return Scaffold(
appBar: AppBar(
title: Text("Widget Lifecycle Sample"),
centerTitle: true,
),
body: Column(
children: <Widget>[
Center(
child: RaisedButton(
textColor:Colors.white,
color: Colors.lightBlue,
child: Text("parent->setState:$_count",style: TextStyle(color: Colors.white),),
onPressed: (){
setState(() {
Log.info(TAG, "parent setState");
_count++;
});
}),
),
SubWidgetLifecycleSamplePage(),
],
)
);
}
@override
void didUpdateWidget(WidgetLifecycleSamplePage oldWidget) {
super.didUpdateWidget(oldWidget);
Log.info(TAG, "parent didUpdateWidget");
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
Log.info(TAG, "parent didChangeDependencies");
}
@override
void deactivate() {
super.deactivate();
Log.info(TAG, "parent deactivate");
}
@override
void reassemble() {
super.reassemble();
Log.info(TAG, "parent reassemble");
}
@override
void dispose() {
super.dispose();
Log.info(TAG, "parent dispose");
}
}
/// 子Widget
class SubWidgetLifecycleSamplePage extends StatefulWidget {
@override
_SubWidgetLifecycleSamplePageState createState() {
Log.info(TAG, "sub createState");
return _SubWidgetLifecycleSamplePageState();
}
}
其中子 Widget 實現與父 Widget 實現相似就不貼了,可在公眾號後台回復關鍵字【Lifecycle】獲取完成源碼,上述代碼顯示效果如下:
其對應的生命週期方法調用如下:
分析如下:
- Widget 初始化流程指的是進入這個 Widge 所在頁面的生命週期流程,先父 Widget 後子 Widget,然後都依次調用 createState、initState、didChangeDepandencies、build 方法;
- 當父 Widget seteState 更新數據時觸發了父 Widget 的構建,故子 Widget 調用了 didUpdateWidget 方法;
- 其他流程比如熱重載、Widget 銷毀都與上面的 State 生命週期方法流程圖相同,這裡不再贅述。