InheritedWidgeを使って、カウンターアプリを作ってみた

image

Version

  • Flutter 2.6.0
  • Dart 2.14.4

InheritedWidgetとは

InheritedWidgetは、祖先のWidgetが管理するデータを複数の子孫が効率的に参照するために使われるWidgetで、providerパッケージのもとになっているらしい、、、です。
Flutterの状態管理を学ぶためにInheritedWidgetを使って、カウンターアプリを作ってみました。

InheritedWidgetの特徴

  • Widgetツリーの下層のBuildContextから、プロパティにO(1)でアクセスする。
  • プロパティが変化した時に下層のBuildContextに更新を伝えてリビルドさせる。

InheritedWidgetの実装例

MyInheritedWidget()

  • MyInheritedWidget of(BuildContext context)で、child:で指定したChildWidget()以下の子Widgetに直接変数を渡す。
  • context.dependOnInheritedWidgetOfExactTypeで、変数に変化があった時リビルドする。
  • bool updateShouldNotify(MyInheritedWidget oldWidget)で、リビルドをコントロールできる。(今回の実装では偶数のみリビルドさせています)
class MyInheritedWidget extends InheritedWidget {
  final int plusCount;

  MyInheritedWidget({required this.plusCount, required Widget child})
      : super(child: child);

  // O(1)でInheritedWidgetを返す
  // Widgetに変更があった時に下位ツリーのリビルドを発生させられる
  static MyInheritedWidget of(BuildContext context) =>
      context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>()
          as MyInheritedWidget;
  

  //更新確認(条件により更新するかしないかをコントロールできる)
  // updateShouldNotifyがtrueを返した時だけ更新が伝播される
  
  bool updateShouldNotify(MyInheritedWidget old) {
    // return plusCount != old.plusCount; // 通常のカウント
    return plusCount % 2 == 0; // 偶数のときだけリビルドする
  } 
}

InheritedWidgetPage()

  • StatefulWidgetでStateを持つ。
  • InheritedWidgetで囲まれた、FloatingActionButtononPressedされると,setStateにより変数が変化し、ChildWidget()以下の子Widgetに伝播する。
class InheritedWidgetPage extends StatefulWidget {
  InheritedWidgetPage({Key? key}) : super(key: key);

  final Widget child = _ChildWidget();

  
  _InheritedWidgetPageState createState() => _InheritedWidgetPageState();
}

class _InheritedWidgetPageState extends State<InheritedWidgetPage> {
  int _plusCount = 0;

  void _change() {
    setState(() {
      _plusCount++;
    });
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("InheritedWidget"),
      ),
      body: Stack(
        children: [
          MyInheritedWidget(
              plusCount: _plusCount, child: Center(child: widget.child)),
          Positioned(
            top: 100,
            right: 50,
            child: FloatingActionButton(
              child: Icon(Icons.add),
              onPressed: _change,
            ),
          ),
        ],
      ),
    );
  }
}

ChildWidget()

  • MyInheritedWidget.of(context)により、上位のInheritedWidgetから直接変数を受け取ることができる。
  • 変数が変化した場合、InheritedWidgetにより再描画される。
class _ChildWidget extends StatelessWidget {
  
  Widget build(BuildContext context) {
    // 変更があった時にリビルドをする
    final plusCount = context
        .dependOnInheritedWidgetOfExactType<MyInheritedWidget>()
        ?.plusCount;

    return Column(
      children: [
        Text(
          plusCount.toString(),
          style: TextStyle(fontSize: 36),
        ),
      ],
    );
  }
}

まとめ

これで、カウンターアプリをInhereitedWidgetで書き換えることができました。 デバックで変数の変化などを見ながら動かすともっとよくわかるかもしれません。

参考

https://medium.com/flutter-jp/inherited-widget-37495200d965 https://qiita.com/agajo/items/375d5415cb79689a925c