Flutterのライフサイクルは下記のように分かれています。
- createState
- mounted is true
- initState
- didChangeDependencies
- build
- didUpdateWidget
- setState
- deactivate
- dispose
- mounted is false
➀createState(Stateを作成する)
まず、FlutterのSDKはStatefulWidgetのcreateStateを呼び出し、Stateを作成します。
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() = _MyHomePageState();
}
class _MyHomePageState extends State MyHomePage {
.....
}
➁mounted is true(Stateをマウントする)
➀にて作成されたStateはBuildContextに紐づけられます。
BuildContextとは端的に言うと、**各WidgetのWidget Tree上の位置(そのWidgetがどこにあるのか)**を制御するためのクラスです。
このタイミングで、Stateオブジェクトはマウントされたと認識されます。
➂initState(Stateを初期化する)
クラスのコンストラクターが呼び出されたあと、すぐにこのinitStateが呼ばれます。
最初に一度だけ実行されます。
オーバーライドできますが、その際は必ず親クラスのinitStateも呼び出す必要があります。
// これを記述することでオーバーライドと認識される
@override
initState() {
// 必ず、super.initState()を呼ぶこと
super.initState();
// Streamをlistenしてデータを連続したデータを受け取れるようにする
cartItemStream.listen((data) {
_updateWidget(data);
});
}
④didChangeDependencies
initStateを呼び出した後にはdidChangeDependenciesが呼び出されます。
didChangeDependenciesは、InheritedWidgetのBuildContext.inheritFromWidgetOfExactTypeが実行された場合に呼び出されます。
InheritedWidgetとは、Treeの下位のWidgetから直接参照できるWidgetのことです。
例
それぞれの開発のフェーズ(開発・検収・本番など)に応じて接続するURLを変えたい場合、InheritedWidgetを用いて上位のWidgetでデータを設定しておいて、それを下位のWidgetからアクセスする。こうすることで、毎回親Widgetから子Widgetへパラメーターを渡す手間を省くことができます。
アクセス方法は、ConfigInheritedWidget.of(context).hogeUrlのような形で情報を取得します。
ただその場合contextにアクセスしたいのですが、initState内ではまだcontextにアクセスすることができません。
そこで、didChangeDependencies内でこれを実行し、InheritedWidgetからデータを取得するようにします。
➄build
build関数はほぼ確実に使われます。描画したいWidgetを記述するのに用います。
@override
Widget build(BuildContext context) {
return Widget();
}
➅didUpdateWidget(Widget oldWidget)
親Widgetが変更されて(親Widgetから渡されるパラメーターの値が代わり)、再ビルドされる必要がある場合にこのdidUpdateWidgetが呼ばれます。
FlutterではStateは使い回しますが、initStateで初期化をしたデータは再び初期化しなければならない場合があります。
例
もしStateのbuild関数がStreamや他のオブジェクトに依存していた場合、古いオブジェクトを購読解除し、didUpdateWidgetで作られた新しいオブジェクトを購読し直すようにすることができます。
これが呼ばれたのちにbuild関数も呼び出されるため、setStateをこの関数内に記述する必要はありません。
➆setState
State内の変数の値を変更したい場合に呼び出されます。これが呼ばれた後、build関数が再実行されます。
この関数は、同期的なコールバックを持ちます。これは、再描画が安価であるため、必要に応じて頻繁に呼び出すことができるためです。
void updateProfile(String name) {
setState(() => this.name = name);
}
➇deactivate
これはほとんどの場合、使われません。Stateがツリーから削除される場合に呼び出されます。
➈dispose
Stateオブジェクトが削除されるとdisposeが呼び出されます。これは永続的なものです。
このメソッドでは、以下のようなものを取り消す場合に用いられます。
- アニメーションの購読
- Streamの購読
- コントローラーの参照
➉mounted is false
disposeが呼び出されると、Stateオブジェクトは再マウントされません。
ここでsetState関数を呼び出した場合、エラーとなります。