【備忘録】Flutterの主要ウィジェットについて

今後モバイルアプリ開発に関わる可能性があるため、代表的なウィジェットの特徴を簡潔にまとめてみました。

| ウィジェット | 状態管理 | 動的UI更新 | シンプルさ | パフォーマンス | Providerの使用 |
|---------------------------|----------|------------|------------|---------------|----------------|
| StatelessWidget | × | × | 〇 | 〇 | × |
| StatefulWidget | 〇 | 〇 | × | △ | × |
| ConsumerWidget | 〇 | 〇 | △ | 〇 | 〇 |
| StatefulConsumerWidget | 〇 | 〇 | × | △ | 〇 |

## 1. StatelessWidget
状態を持たないウィジェット。ウィジェットの状態が変わらない場合に使用される。
静的なUI要素や、状態が外部から提供される場合に適している。

**メリット**
・シンプルで軽量
・状態管理が不要なため、コードが簡潔
・パフォーマンス〇
**デメリット**
・状態を持たないため、動的なUIの更新ができない

```dart
import 'package:flutter/material.dart';

class MyStatelessWidget extends StatelessWidget {
  final String title;

  MyStatelessWidget({required this.title});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: Center(
        child: Text('This is a StatelessWidget'),
      ),
    );
  }
}
```


## 2. StatefulWidget
内部に状態を持つウィジェット。状態が変化する場合に使用される。
動的に変化するUIに適している。

メリット:
・動的なUIの更新が可能
・状態管理がウィジェット内で完結する
デメリット:
・コードが複雑になる可能性がある
・パフォーマンス△

```dart
import 'package:flutter/material.dart';

class MyStatefulWidget extends StatefulWidget {
  @override
  _MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}

class _MyStatefulWidgetState extends State<MyStatefulWidget> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('StatefulWidget Example'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('You have pushed the button this many times:'),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}
```


## 3. ConsumerWidget
ConsumerWidgetは、Providerを使用して状態管理を行うウィジェット。
Providerから状態を取得し、UIを更新することができる。
メリット:
・Providerを使用することで、状態管理が容易
・状態の変更に応じて、必要な部分のみを再ビルドできる
デメリット:
・Providerの理解が必要

```dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

final counterProvider = StateProvider<int>((ref) => 0);

class MyConsumerWidget extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final counter = ref.watch(counterProvider);

    return Scaffold(
      appBar: AppBar(
        title: Text('ConsumerWidget Example'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('You have pushed the button this many times:'),
            Text(
              '$counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => ref.read(counterProvider.notifier).state++,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}
```


## 4. StatefulConsumerWidget
StatefulWidgetとConsumerWidgetの機能を組み合わせたウィジェット。
内部に状態を持ちながら、Providerからの状態も利用できる。
メリット
・内部状態と外部状態の両方を管理できる
・柔軟な状態管理が可能
デメリット:
・複雑な状態管理が必要なため、コードが複雑になる可能性がある
・Providerの理解とセットアップが必要

```dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

final counterProvider = StateProvider<int>((ref) => 0);

class MyStatefulConsumerWidget extends StatefulWidget {
  @override
  _MyStatefulConsumerWidgetState createState() => _MyStatefulConsumerWidgetState();
}

class _MyStatefulConsumerWidgetState extends State<MyStatefulConsumerWidget> {
  @override
  Widget build(BuildContext context) {
    return Consumer(
      builder: (context, ref, child) {
        final counter = ref.watch(counterProvider);

        return Scaffold(
          appBar: AppBar(
            title: Text('StatefulConsumerWidget Example'),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text('You have pushed the button this many times:'),
                Text(
                  '$counter',
                  style: Theme.of(context).textTheme.headline4,
                ),
              ],
            ),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: () => ref.read(counterProvider.notifier).state++,
            tooltip: 'Increment',
            child: Icon(Icons.add),
          ),
        );
      },
    );
  }
}
```

### まとめ
パフォーマンスを考えると、UI部分をStatelessWidgetで作成してデータ更新などで再描画が必要な部分のみConsumerWidgetを使用するのが一番よさそう

参考:
[StatefulWidget & StatelessWidget](https://flutter.ctrnost.com/tutorial/tutorial05/#statefulwidget--statelesswidget)
[ConsumerWidget](https://pub.dev/documentation/flutter_riverpod/latest/flutter_riverpod/ConsumerWidget-class.html)
[StatefulConsumerWidget](https://riverpod.dev/ja/docs/concepts/reading#statefulwidgetstate-%E3%81%AE%E4%BB%A3%E3%82%8F%E3%82%8A%E3%81%AB-consumerstatefulwidgetconsumerstate-%E3%82%92%E7%B6%99%E6%89%BF%E3%81%99%E3%82%8B)