StatefulWidgetが状態を持ってるというのは間違ってる？

どうも、新卒エンジニアになって1ヶ月が経過しました。
なおちんです。

最近仕事でFlutterを使うことになったため、勉強を始めたのですが、Flutterで超重要なStatelessWidgetとStatefullWidgetについて調べたところ、多くの方が**StatelessWidgetは状態を持たない、StatefullWidgetは状態をもつ**Widgetと説明をされていました。

StatelessWidgetとStatefullWidgetについてさらに調べると、この説明（特にStatefullWidget）は果たして正しいのか？と疑問を持ったため、今回この記事を書いてみました。

## StatelessWidgetとStatefullWidgetとは

### StatelessWidget
まず、StatelessWidgetgとはなんなのか、公式ドキュメントには下記のように記載されています。

>A stateless widget is a widget that describes part of the user interface by building a constellation of other widgets that describe the user interface more concretely. The building process continues recursively until the description of the user interface is fully concrete (e.g., consists entirely of RenderObjectWidgets, which describe concrete RenderObjects).

>Stateless widget are useful when the part of the user interface you are describing does not depend on anything other than the configuration information in the object itself and the BuildContext in which the widget is inflated. For compositions that can change dynamically, e.g. due to having an internal clock-driven state, or depending on some system state, consider using StatefulWidget.

ざっくりと訳すと、**StatelessWidgetは他のWidgetを組み合わせることによって、UIの一部を構築することができるWIdgetで、一度構築したら再構築されないWidgetだよ**ということです。

一般的には、ユーザーの動作によって画面が変わったりしない静的なページを作るときに使われます。

### StatefullWidget
一方でstatefullWidgetの説明は下記のようになっています。
>A stateful widget is a widget that describes part of the user interface by building a constellation of other widgets that describe the user interface more concretely. The building process continues recursively until the description of the user interface is fully concrete (e.g., consists entirely of RenderObjectWidgets, which describe concrete RenderObjects).

>Stateful widgets are useful when the part of the user interface you are describing can change dynamically, e.g. due to having an internal clock-driven state, or depending on some system state. For compositions that depend only on the configuration information in the object itself and the BuildContext in which the widget is inflated, consider using StatelessWidget.

一文目は同じですね。
二文目をざっくり訳すと、**StatefullWidgetは時間やシステムの状態（ユーザーによる操作など）によって動的にUIを変化させるときに便利なWidgetだよ**ということです。

例えば、ユーザーがボタンをクリックしたら、メニューを表示するなどというときはこのStatefullWidgetを使います。

## StatelessWidgetとStatefullWidgetのライフサイクル
StatelessWidgetとStatefullWidgetの違いがざっくりとわかったところで、なぜStatelessWidgetは一度しかビルドされず、StatefullWidgetは再ビルドされるのか、ソースコードを見ながらそれぞれのWidgetのライフサイクルを理解しましょう。

### StatelessWidget
```dart
class SampleStatelessWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}
```
StatelessWidgetを使う際はそれを継承したクラス（今回はSampleStatelessWidget）を定義します。

SampleStatelessWidgetクラスはとてもシンプルなクラスでbuildという名前のメソッドだけを持っています。このbuild()メソッドは、Widgetを構築する際に呼ばれるメソッドです。

そして、このSampleStatelessWidgetクラスは呼ばれたときに一度だけbuild()メソッドを実行します。
それ以降は自分自身でbuild()メソッドを実行しなおすことはできません。

つまり、**一度画面を構築してしまったら再度構築しなおすことができない**ということです。

### StatefullWidget
```dart
class SampleStatefulWidget extends StatefulWidget {
  @override
  _SampleStatefulWidgetState createState() => _SampleStatefulWidgetState();
}

class _SampleStatefulWidgetState extends State<SampleStatefulWidget> {
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}
```
続いてStatefullWidgetを継承したSampleStatefullWidgetクラスです。

こちらのクラスはStatelessWidgetのときと違い、build()メソッドを持ちません。
その代わりに、createState()メソッドを持ち、そのメソッド内で別のクラス（SampleStatefulWidgetState）を呼び出しています。
そしてこれが、**StatefullWidgetが状態を持つことができる要因**でもあります。

SampleStatefulWidgetStateの継承元であるStateクラスはbuild()メソッド以外に下記のような状態管理をするためのメソッドが定義されています。
#### initState()
initState()メソッドはWidgetツリーへ追加されたときに初めに1度だけ呼ばれるメソッドです。
なんらかの初期化を行いたいときにオーバーライドして呼び出します。

#### didChangeDependencies()
initState()メソッドが呼ばれたあとに呼ばれるメソッドです。

#### setState()
任意のタイミングに呼び出すことができるメソッドで、setState内でStateクラス内の値を変化させることで、再度build()メソッドを呼び出し、Widgetを再構築することができます。

#### didUpdateWidget()
didUpdateWidget()メソッドは親Widgetが再構築されて、コンストラクタに渡ってくる値が変化するときに呼ばれるメソッドです。
didUpdateWidget()メソッドがよばれると、build()メソッドが呼ばれ、Widgetが再構築されます。（setState()メソッドを呼び出す必要はありません）

#### despose()
Widgetが破棄されたときに実行されるメソッドです。
なんらかの後始末が必要なときにオーバーライドして呼び出します。

以上のライフサイクルを図にまとめると以下のようになります。
![図1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601599/3c36b92d-fd4d-7385-709b-4956544f0246.png)




## StatefullWidgetはただの外装
上記の通り、StatefullWidgetは**Stateクラスの様々なメソッドによって**状態が管理されています。

つまり、
**StatefullWidget単体はただの外装に過ぎず、Stateクラスの存在によってその形を変えることができている**
ということです。

StatefullWidgetは状態を持つWidgetという説明は間違いではないけど、正確に答えるとStatefullWidget自体が状態管理できるわけではないよというお話でした。