Flutterの学習で詰まるポイント(1)宣言的UI で書いたように、UIを初期化してからその属性を更新するという方法は取りません。宣言的UIをサポートしているフレームワークでは、UIを初期化した後で、その属性を何かのイベントで書き換えることが出来ない。
FlutterでUIを変更するには、「自分が参照しているデータは更新されたので、UIを再構築(リビルド)しなさい」とWidgetに伝えます。簡単に言うと、Widgetのbuildメソッドを何度も呼び出すことになります。
すごく単純化すると、タップされた数をカウントする機能があって、それを画面に出すコードがあるとします。Text(counter)
という形です。Flutterは、以下の理屈でUIを更新します。
- ボタンタップで、counter++する
- counterが変更されたので、Flutterにbuildの呼び出しを依頼する
- Flutterは更新されたcounterで、UIを作り直す
- 更新されたcounterによって、UIが表示される
buildを何度も呼び出すと行っても、onPressed: () => build()
と書けるわけではありません。build
を呼び出すのは、Flutterのフレームワーク側に委ねられます。buildを呼び出すには、Flutterに「このWidgetが参照しているデータが変更されたので、もう1回buildを呼び出す」という依頼を投げることだけです。
データが変更されたことを検知してBuildを呼び出す仕組みに関わってくるのが、Stateです。
Stateって何
Stateは、Widgetのライフサイクルを表現したものです。UIにライフサイクルという概念があることに馴染みがない人が、特にFlutterが初めてのフロントエンド・スマホアプリ開発の人に多い。教えていて、強く感じます。ま、HTMLにライフサイクルが言ってもなんのこっちゃ、ですよね。
Flutterが提唱しているWidgetのライフサイクルは、簡単に言うとこの4つです。
- Stateが生成された(created)
- 生成されたStateとWidgetが紐づいた(initilized)
- Widgetのbuildを呼び出せるようになった(ready)
- WidgetがWidget Treeから破棄された(defunct)
これを図式化したのが、こちらの画像です。

setState
上記の図の右側に注目してください。ready→(強制的にウイジェットを再構築)→ setState → buildと書かれていると思います。setStateを呼び出すことで、buildを呼び出すことができます。
floatingActionButton: FloatingActionButton(
onPressed:() => setState( () => _counter++;),
tooltip: 'Increment',
child: const Icon(Icons.add),
),
setStateに引数も戻り値も持たない関数を代入して、関数の内部で自分が参照しているデータを更新します。そうすると、新しいデータでbuildが実行されます。build(1), build(2) というイメージを持つと、わかりやすいかもしれません。
Flutterにおいては、上記のStateを自分が好きなように管理できる機能を持ったWidgetのことを、StatefulWidgetと言います。createState, initState, setState, dispose, などが利用できます。
この逆で、Stateに対するイベントハンドラが一切利用できないWidgetのことを、StatelessWidgetと言います。Widgetのライフサイクルに一切タッチすることができない=Stateless、という意味合いです。
ここに書いてある内容は、プログラムの書き方ではありません。Flutterの設計思想の説明です。設計思想が理解できるようになるには、ある程度アプリ開発の経験がどうしても必要です。自分が書いてきたコードの背景には、こういう考えがあったんだなと腑に落ちるまで、時間がかかるため。
Flutterのコードをなぞることはできても、新しい設計思想や考え方を提案できなければ、レベルが上がりません。レベルを上げるには、自分なりに抽象的な概念を図式化する・構造化する訓練が必要です。
次回は、Flutterのサンプルアプリにもカウンターアプリケーションを見ながら、StatefulWidgetとStatelessWidgetの違いをコードで整理し、Stateのライフサイクルについての理解を深められるようにします。