FlutterのStatelessWidgetとStatefulWidgetの違い

# 📖参考

[Flutter | Introduction to widgets]
(https://flutter.dev/docs/development/ui/widgets-intro)
[KotlinとJavaができる人向けDart速習]
(https://qiita.com/kikuchy/items/2cce118d38fc15324b2b)
[Dart言語の3つのコンストラクタ]
(https://makicamel.hatenablog.com/entry/2019/03/14/2139330)
[Language tour | Dart]
(https://dart.dev/guides/language/language-tour)
[Flutter：StatelessWidgetとStatefulWidgetの違い]
(https://qiita.com/rindarinda5/items/496c70e54784921a31f0)
[Flutter WidgetにKeyが必要な理由, 仕組みについて](https://qiita.com/kurun_pan/items/f91228cf5c793ec3f3cc)
[FlutterのUIウィジェットを理解するための基本を知ろう！]
(https://codezine.jp/article/detail/13329)
[Flutter 全部俺 Advent Calendar 2019]
(https://adventar.org/calendars/4140)
[FlutterにおけるWidgetとElementとRenderObjectの基本]
(https://engineer.recruit-lifestyle.co.jp/techblog/2019-12-24-flutter-rendering/)

# 🧐途中で見つけた面白そうな記事

[Dartのconstコンストラクタとは]
(https://zenn.dev/razokulover/articles/61380323a73e00572789)
[Flutter の Widget ツリーの裏側で起こっていること]
(https://medium.com/flutter-jp/dive-into-flutter-4add38741d07)

# Widget vs. Element vs. RenderObject

| クラス | 説明 |
|:-:|:--|
| Widget | **自身のUIの構成情報**と**子Widget**をfinalなフィールドとして保持するImmutableなクラス。`createElement()`メソッドによってElementを生成し、`createRenderObject()`によってRenderObjectを生成する。 |
| Element | **UIの状態(State)**を保持するMutableなクラス。Element同士を集約するだけの機能をもつComponentElementと、RenderObjectを生成するRenderObjectElementに分類される。`mount()`メソッド内の`attachRenderObject()`メソッドによって、Widgetから受け取ったRenderObjectをRenderObjectツリーに挿入する。 |
| RenderObject | **UIの描画**を行うMutableなクラス。RenderObjectツリーの情報をもとに、スクリーンにUIを描画する。 |

# StatelessWidget vs. StatefulWidget

**StatelessWidget**は、状態(State)を持たない静的Widgetであり、フィールドは全てfinalで保持される。
`StatelessWidget`を継承するWidgetでは、**定数コンストラクタ**(※後述)と、Widgetを生成する`build()`メソッドをオーバーライドする。

```dart:StatelessWidget
class StatelessWidgetSample extends StatelessWidget {
  // 定数コンストラクタ
  const StatelessWidgetSample({Key? key,}) : super(key: key);

  // build()
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: StatelessWidgetSample2(),
    );
  }
}
```

**StatefulWidget**は、状態を持つ動的Widgetであり、ある瞬間(状態)における画面を構成するWidgetオブジェクトと、次に`build()`が呼び出されるまで半永続的に情報を保持するStateオブジェクトに分類される。
`StatefulWidget`を継承するWidgetでは、**定数コンストラクタ**と、Stateを生成する`createState()`メソッドをオーバーライドする。

```dart:StatefulWidget
// StatefulWidget
class StatefulWidgetSample extends StatefulWidget {
  // 定数コンストラクタ
  const StatefulWidgetSample({Key? key}) : super(key: key);

  // createState()
  @override
  _StateSample createState() => _StateSample();
}
```

`State<T where StatefulWidget>`を継承するStateには、Stateの値を変化させる`setState()`メソッドを記述し、Widgetを生成する`build()`メソッドをオーバーライドする。

```dart:State
// State
class _StateSample extends State<StatefulWidgetSample> {
  // 変数フィールド
  int _counter = 0;

  // フィールドの値を変化させるメソッド → Stateの内部状態が変化
  void _increment() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Row(
      children: <Widget>[
        ElevatedButton(
          onPressed: _increment,
          child: const Text('Increment'),
        ),
        Text('Count: $_counter'),
      ]
    );
}
```

## 定数コンストラクタ(Constant Constructor)

定数コンストラクタは、インスタンスを**コンパイル時定数(compile-time constant)**として生成したいクラスに対して採用する。
コンパイル時定数にする以上、フィールドは**定数**であることが保証されている必要があるため、**全てのフィールドがfinalである場合にのみ**定数コンストラクタを利用することができる。


```dart:Constant_Constructor
class Point {
  final int x;
  final int y;

  // 定数コンストラクタ
  const Point(this.x, this.y);
}

// 定数コンストラクタを採用したクラスは、constキーワードを付けて「コンパイル時定数」として宣言できる
var p = const Point(1, 2);
var q = const Point(1, 2);
var r = Point(1, 2);

identical(p, q);  // true ← コンパイル時定数は、等価であるインスタンスを使い回すため等値になる
identical(q, r);  // false ← (実質的に)実行時定数は、等価であってもインスタンスを新たに生成するため等値にならない
```

### Widgetのコンストラクタについて

Flutter公式のWidgetのコンストラクタの記法が見慣れないため、ここで解説する。

```dart:コンストラクタ
const StatelessWidgetSample({Key? key,}) : super(key: key);
```

`const`キーワードが使われていることから、インスタンス生成時もconstキーワードを付けて定数オブジェクトとして生成しなければならない**定数コンストラクタ**であることが分かる。

次に、コンストラクタ引数`{Key? key,}`の部分は、`const StatelessWidgetSample(key: KeySample())`のように、インスタンス生成時にオプションで引数ラベルkeyにOptional\<Key>型の値を設定できる、**[名前付き任意パラメータ(Optional Named Parameter)](https://qiita.com/kikuchy/items/2cce118d38fc15324b2b#%E5%90%8D%E5%89%8D%E4%BB%98%E3%81%8D%E4%BB%BB%E6%84%8F%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BFoptional-named-parameter%E3%81%A8%E9%A0%86%E5%BA%8F%E4%BB%98%E3%81%8D%E4%BB%BB%E6%84%8F%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BFoptional-positional-parameter)**である。

最後に、コロン`:`の部分はブロック`{}`を省略する代わりに用いられ、**親クラスの(デフォルトコンストラクタでない)コンストラクタ**を明示的に呼び出す特殊な記法で記述されている。

> By default, a constructor in a subclass calls the superclass’s unnamed, no-argument constructor. The superclass’s constructor is called at the beginning of the constructor body.

> (中略)

> If the superclass doesn’t have an unnamed, no-argument constructor, then you must manually call one of the constructors in the superclass. Specify the superclass constructor after a colon (:), just before the constructor body (if any).

> Invoking a non-default superclass constructor － Language tour | Dart より引用

#### Widgetのコンストラクタに関する補足①(クラス継承)

**子クラスは親クラスのコンストラクタを必ず呼び出さなければならない**という決まりがあるため、親クラスのコンストラクタを明示的に呼び出さない場合、子クラスは親クラスのデフォルトコンストラクタを呼び出す。[^1]

[^1]: ただし、親クラスで他の(引数が**必要**な)コンストラクタが記述されている場合は(引数の**不要**な)デフォルトコンストラクタが自動的に生成されていないため、子クラスで親クラスのコンストラクタを指定して呼び出す必要がある。

ここで、[StatelessWidget](https://api.flutter.dev/flutter/widgets/StatelessWidget-class.html)も[StatefulWidget](https://api.flutter.dev/flutter/widgets/StatefulWidget-class.html)もコンストラクタは以下のように定義されており、名前付きパラメータkeyは**任意パラメータ**である(=引数がなくてもよい)ため、子クラスで暗黙的に`super()`[^2][^3]が呼び出せる。

[^2]: コンストラクタ引数の`{}`ブロックは**任意パラメータ(Optional Parameter)**を表すため、`super()`のように引数がなくてもよい。
[^3]: StatelessWidget / StatefulWidgetはどちらも**Widget**クラスを親クラスとしている。

```dart:Constructor
// StatelessWidget
const StatelessWidget({Key? key}) : super(key: key);

// StatefulWidget
const StatefulWidget({Key? key}) : super(key: key);
```

なお、Javaと同様、Dartにおける全てのクラスは**Object**クラスのサブクラスとして定義される。

#### Widgetのコンストラクタに関する補足②(Key)

Widgetのコンストラクタに任意パラメータとして定義されているKeyは、ウィジェットツリー内のWidgetを一意に識別する際に利用され、既定値は`null`である。