在 Flutter 中,有兩類常用的 Widget:
無狀態的 StatelessWidget
有狀態的 StatefulWidget
在開發過程中,我們經常需要繼承它們兩來實現自己的 Widget。
1. StatelessWidget
一個 StatelessWidget 是不能被改變的,比如:Icon
、Text
等。
如果你的控制元件一旦顯示,就不需要再做任何的變更,那麼你應該使用 StatelessWidget。
實現一個自己的 StatelessWidget 很簡單。
當你看到下面這個例子?時,你就知道它有多簡單了。
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return _buildMyWidget(context);
}
複製程式碼
看,只要在 build()
中返回你的檢視就可以了。
2. StatefulWidget
一個 StatefulWidget 是有狀態的,可變的。
它可以改變自己的外觀,以響應使用者的操作或者資料的變化。
比如:CheckBox
、Switch
..
我們之所以能夠改變一個 StatefulWidget,是因為它有一個設定狀態的函式:
setState((){
// 更新狀態、資料
})
複製程式碼
呼叫這個函式後,就會觸發 StatefulWidget 的檢視樹重建。
因此,當我們需要一個可互動的,即能根據使用者操作或資料變化而改變檢視的 Widget 時,那就得用上 StatelessWidget 了。
2.1 自定義 StatefulWidget
現在,來建立一個自定義的 StatefulWidget:
實現一個 StatefulWidget,它是 Widget 的外層。
class FavoriteStatefulWidget extends StatefulWidget { // 必須重寫 createState(),返回一個 State,它包含了檢視和互動邏輯 @override State<StatefulWidget> createState() => _FavoriteStatefulWidgetState(); } 複製程式碼
實現一個 State,它提供了真正的 Widget 檢視和互動邏輯。
class _FavoriteStatefulWidgetState extends State<FavoriteStatefulWidget> { bool _isFavorited = true; int _favoriteCount = 41; void _toggleFavorite() { // 通過 setState() 更新資料 // 元件樹就會自動重新整理了 setState(() { if (_isFavorited) { _favoriteCount -= 1; _isFavorited = false; } else { _favoriteCount += 1; _isFavorited = true; } }); } // 重寫 build() 函式,構建檢視樹 @override Widget build(BuildContext context) => Row( mainAxisSize: MainAxisSize.min, children: [ Container( padding: EdgeInsets.all(0), child: IconButton( icon: (_isFavorited ? Icon(Icons.star) : Icon(Icons.star_border)), color: Colors.red[500], onPressed: _toggleFavorite, ), ), SizedBox( width: 18, child: Container( child: Text('$_favoriteCount'), ), ), ], ); } 複製程式碼
使用這個自定義的 StatefulWidget 看看效果。
main() => runApp(MaterialApp( title: 'Flutter Demo', home: Scaffold( appBar: AppBar( title: Text('Flutter Demo'), ), body: Container( color: Colors.white, child: Center( child: FavoriteStatefulWidget(), ), ), ), )); 複製程式碼
2.2 State 的生命週期
從上面的例子中可以看到,StatefulWidget 會要求提供一個含有檢視樹的 State。
既然 State 能夠控制一個檢視的狀態,那它肯定會有一系列的生命週期。
上圖就是 State 的生命週期圖。
StatefulWidget.createState()
Framework 呼叫會通過呼叫
StatefulWidget.createState()
來建立一個 State。initState()
新建立的 State 會和一個 BuildContext 產生關聯,此時認為 State 已經被安裝好了,
initState()
函式將會被呼叫。通常,我們可以重寫這個函式,進行初始化操作。
didChangeDependencies()
在
initState()
呼叫結束後,這個函式會被呼叫。事實上,當 State 物件的依賴關係發生變化時,這個函式總會被 Framework 呼叫。
build()
經過以上步驟,系統認為一個 State 已經準備好了,就會呼叫
build()
來構建檢視。我們需要在這個函式中,返回一個 Widget。
deactivate()
當 State 被暫時從檢視樹中移除時,會呼叫這個函式。
頁面切換時,也會呼叫它,因為此時 State 在檢視樹中的位置發生了變化,需要先暫時移除後新增。
⚠️注意,重寫的時候必須要呼叫
super.deactivate()
。dispose()
當 State 被永久的從檢視樹中移除,Framework 會呼叫該函式。
在銷燬前觸發,我們可以在這裡進行最終的資源釋放。
在呼叫這個函式之前,總會先呼叫
deactivate()
。⚠️注意,重寫的時候必須要呼叫
super.dispose()
。didUpdateWidget(covariant T oldWidget)
當 widget 的配置發生變化時,會呼叫這個函式。
比如,Hot-reload 的時候就會呼叫這個函式。
這個函式呼叫後,會呼叫
build()
。setState()
當我需要更新 State 的檢視時,需要手動呼叫這個函式,它會觸發
build()
。