具狀態元件
瞭解 StatefulWidget 以及如何重新建置 Flutter UI。
瞭解元件何時需要具有狀態,以及如何使用 setState 觸發 UI 更新。
你將完成的事項
Steps
1
簡介
簡介
到目前為止,你的應用程式顯示了一個格狀方格和一個輸入欄位,但方格尚未更新以反映使用者的猜測。當此應用程式完成時,下一個未填滿列中的每個方塊,應在每次提交使用者猜測後更新,具體方式為:
- 顯示正確的字母。
- 變更顏色以反映字母是否正確(綠色)、字母存在於單詞中但位置錯誤(黃色),或字母完全不在單詞中(灰色)。
為了處理這種動態行為,你需要將 GamePage 從 StatelessWidget 轉換為 StatefulWidget。
2
為何需要具狀態元件?
為何需要具狀態元件?
當元件 (Widget) 的外觀或資料需要在其生命週期內改變時,你需要使用 StatefulWidget 以及配套的 State 物件。雖然 StatefulWidget
本身仍然是不可變的(其屬性在建立後無法更改),但 State 物件是長期存在的,可以保存可變的資料,並且當資料發生變化時可以重新建置,從而使 UI 更新。
例如,以下的元件樹展示了一個簡單的應用程式範例,該應用程式使用具狀態元件,其計數器會在按下按鈕時遞增。
以下是基本的 StatefulWidget 結構(目前尚未執行任何操作):
class ExampleWidget extends StatefulWidget {
ExampleWidget({super.key});
@override
State<ExampleWidget> createState() => _ExampleWidgetState();
}
class _ExampleWidgetState extends State<ExampleWidget> {
@override
Widget build(BuildContext context) {
return Container();
}
}
3
將 GamePage 轉換為具狀態元件
將 GamePage 轉換為具狀態元件
要將 GamePage(或任何其他)元件從無狀態元件轉換為具狀態元件,請執行以下步驟:
- 將
GamePage改為繼承StatefulWidget而非StatelessWidget。 -
建立一個名為
_GamePageState的新類別,繼承State<GamePage>。這個新類別將保存可變的狀態和build方法。將build方法以及所有在元件上實例化的屬性從GamePage移至狀態物件。 - 在
GamePage中實作createState()方法,該方法會回傳_GamePageState的實例。
修改後的程式碼應如下所示:
class GamePage extends StatefulWidget {
GamePage({super.key});
@override
State<GamePage> createState() => _GamePageState();
}
class _GamePageState extends State<GamePage> {
final Game _game = Game();
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
for (var guess in _game.guesses)
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
for (var letter in guess)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 2.5, vertical: 2.5),
child: Tile(letter.char, letter.type),
)
],
),
GuessInput(
onSubmitGuess: (_) {
// TODO, handle guess
print(guess); // Temporary
},
),
],
),
);
}
}
4
使用 setState 更新 UI
使用 setState 更新 UI
每當你修改 State 物件時,你必須呼叫 setState
來通知框架更新使用者介面並再次呼叫 build 方法。
在此應用程式中,當使用者進行猜測時,猜測的單詞會儲存在 Game 物件上,該物件是 GamePage 類別的屬性,因此是可能會變更並需要 UI 更新的狀態 (state)。當此狀態被修改時,方格應重新繪製以顯示使用者的猜測。
要實作這一點,請更新傳遞給 GuessInput 的回呼(callback)函式。該函式需要呼叫 setState,並在 setState
內部執行邏輯以判斷使用者的猜測是否正確。
更新你的程式碼:
class GamePage extends StatefulWidget {
GamePage({super.key});
@override
State<GamePage> createState() => _GamePageState();
}
class _GamePageState extends State<GamePage> {
final Game _game = Game();
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
for (var guess in _game.guesses)
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
for (var letter in guess)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 2.5, vertical: 2.5),
child: Tile(letter.char, letter.type),
)
],
),
GuessInput(
onSubmitGuess: (String guess) {
setState(() { // NEW
_game.guess(guess);
});
},
),
],
),
);
}
}
現在,當你在 TextInput 中輸入合法的猜測並提交時,應用程式將反映使用者的猜測。如果你在未呼叫 setState 的情況下呼叫 _game.guess(guess),內部遊戲資料將會改變,但 Flutter 不會知道需要重新繪製螢幕,使用者也不會看到任何更新。
5
複習
複習
你完成的事項
以下是你在本課程中建置和學習的摘要。瞭解元件何時需要具有狀態
當元件的外觀或資料需要在其生命週期內改變時,你需要 StatefulWidget。元件本身保持不可變,但其配套的 State 物件保存可變資料並觸發重新建置。
將 GamePage 轉換為 StatefulWidget
你透過建立配套的 _GamePageState 類別、將 build 方法和可變屬性移至其中,以及實作 createState(),將
GamePage 重構為具狀態元件。IDE 的快速輔助支援可以自動化此轉換。
使用 setState 讓應用程式回應使用者輸入
呼叫 setState 會告訴 Flutter 重新建置元件的 UI。當使用者提交猜測時,你呼叫 setState 來更新遊戲狀態,方格會自動反映新資料。你的應用程式現在真正具有互動性了!
6
自我測驗
自我測驗
Stateful Widgets Quiz
1 / 2-
當元件擁有超過三個子元件時。
Not quite
子元件的數量不決定元件是否具有狀態。
-
當元件位於元件樹的根部時。
Not quite
根元件可以是無狀態的;是否具有狀態取決於資料是否在元件的生命週期內發生變更。
-
當元件需要發出 HTTP 請求時。
Not quite
HTTP 請求可以從任一種元件發出,但狀態變更需要 StatefulWidget。
-
當元件的外觀或資料需要在其生命週期內改變時。
That's right!
當 UI 必須回應隨時間發生的資料變更時,需要使用 StatefulWidget。
-
元件從元件樹中被移除。
Not quite
元件依然存在;只是在沒有 setState 的情況下不會在視覺上更新。
-
應用程式將崩潰並出現錯誤。
Not quite
應用程式不會崩潰,但 UI 不會更新。
-
Flutter 自動偵測到變更並重新建置 UI。
Not quite
Flutter 需要 setState 才能知道何時重新建置;它不會自動偵測變更。
-
資料在內部發生變更,但 Flutter 不會重新建置 UI 以反映變更。
That's right!
不呼叫 setState 時,Flutter 不知道需要重新繪製,因此使用者不會看到更新。
Unless stated otherwise, the documentation on this site reflects Flutter 3.44.0. Page last updated on 2026-06-14. View source or report an issue.