Roadmap.shで学ぶFlutter -  InheritedWidget -

# 導入
前々回から始めているFlutter学習シリーズ。
[roadmap.shのFlutter](https://roadmap.sh/flutter)の3本目の記事です。
今回はInheritedWidgetについて。

https://api.flutter.dev/flutter/widgets/InheritedWidget-class.html

# InheritedWidget

- InheritedWidgetは、**ツリー内の情報を効率的に伝播**するための基本クラス
    - 要は、Stateの変化があった際に他のWidgetにもその変化を伝えられるという意味と思われる
- BuildContext.dependOnInheritedWidgetOfExactType
    - 特定の種類のInheritedWidgetのインスタンスをビルドコンテキストから取得するのに利用する
    - InheritedWidgetがこの方法で参照されると、Stateの変化でConsumerが再ビルドされる
        - 例
            ~~~dart:FrogColor.dart
            class FrogColor extends InheritedWidget {
              const FrogColor({
                super.key,
                required this.color,
                required super.child,
              });
            
              final Color color;
            
              static FrogColor? maybeOf(BuildContext context) {
                return context.dependOnInheritedWidgetOfExactType<FrogColor>();
              }

              static FrogColor of(BuildContext context) {
                final FrogColor? result = maybeOf(context);
                assert(result != null, 'No FrogColor found in context');
                return result!;
              }
            
              @override
              bool updateShouldNotify(FrogColor oldWidget) => color != oldWidget.color;
            }
            ~~~
## Implementing the of and maybeOf methods
- FrogColorは、InheritedWidgetを継承し、色情報を保持するクラス
    - maybeOfとofという2つの静的メソッドを持ち、BuildContext.dependOnInheritedWidgetOfExactTypeを呼び出す
    - ofメソッドは非nullのインスタンス(result)を返し、InheritedWidgetが見つからない場合はassertを実行する
    - maybeOfメソッドはnullableなインスタンスを返し、見つからない場合はnullを返す
    - ofメソッドの内部でmaybeOfを呼び出す
- 他のクラスでInheritedWidgetが実装される際はPrivateだが、ofとmaybeOfメソッドはPublicクラスで実装されることがある
    - 例：ThemeはプライベートなInheritedWidgetを構築するStatelessWidgetにと実装され、Theme.ofは、BuildContext.dependOnInheritedWidgetOfExactTypeを使ってプライベートなInheritedWidgetを検索してThemeDataを返却する

## Calling the of or maybeOf methods
- ofまたはmaybeOfメソッドを使用する場合、コンテキストはInheritedWidgetの子孫である必要がある
- 例: BuilderのコンテキストがFrogColor(InheritedWidget)の子になっている
```dart
// continuing from previous example...
class MyPage extends StatelessWidget {
  const MyPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: FrogColor(
        color: Colors.green,
        child: Builder(
          builder: (BuildContext innerContext) {
            return Text(
              'Hello Frog',
              style: TextStyle(color: FrogColor.of(innerContext).color),
            );
          },
        ),
      ),
    );
  }
}
```
この場合、innerContext(InheritedWidgetの子であるBuilderのコンテキスト)が使用されており、適切に機能する

- 機能しない例: MyOtherPageのコンテキストがFrogColorウィジェットの親である場合

```dart
// continuing from previous example...

class MyOtherPage extends StatelessWidget {
  const MyOtherPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: FrogColor(
        color: Colors.green,
        child: Text(
          'Hello Frog',
          style: TextStyle(color: FrogColor.of(context).color),
        ),
      ),
    );
  }
}
```
この例では、親のコンテキストのcontextが使用されており、assertが発火されます。

# 終わりに
InheritedWidgetは、Flutterにおける状態管理の基本的な要素であり、ツリー内の情報を効率的に伝播するために重要です。

特に、ofメソッドやmaybeOfメソッドを活用することで、InheritedWidgetのインスタンスを取得し、状態の変化を子ウィジェットに伝えることができることを理解できました。

ofメソッドは、InheritedWidgetのインスタンスを必ず取得するために使われ、該当するInheritedWidgetが存在しない場合はエラーを発生させます。一方、maybeOfメソッドは、該当するInheritedWidgetが存在しない場合にnullを返すため、柔軟なエラーハンドリングが可能です。

これらのメソッドを適切に使い分けることで、InheritedWidgetの利便性と安全性を高めることができるので、今後は意識して使っていこうと思いました。