【Flutter】InheritedWidgetでサンプルを作ったので方法をまとめておく

どーも、ぐるたか@guru_takaです。

ボタンを押すと、数字がカウントされていくサンプルをInherited Widgetで書き直しました。

setStateが呼び出されると、必要なWidgetのみ、ビルドされるようになっています!

ここでは、サンプルに沿って、Inherited Widgetの使い方を紹介します。サンプルソースは下記リンクにありますので、よろしければご覧ください。
参考 gurutaka/inherited_widget_sampleGithub

InheritedWidgetとは?


InheritedWidgetは、ツリーの配下に対し、効率的に情報を伝えるWidgetのクラスです。下の例が最もわかりやすいので、引用します。

すごく簡単な図ですが、Cボタンを押下したら、Stateが変更されて、Aのテキストだけがリビルドされて表示が変わる。

引用:InheritedWidget/InheritedModelとは何か

このように、InheritedWidgetを使うと、効率的に情報をツリー下に渡せます!

参考 InheritedWidget class公式

処理の主な流れ

今回のサンプルの処理の流れに下にまとめてみました。

STEP.1
ボタン押すと、setStateが呼び出される
STEP.2
StatefulWidgetがリビルド
STEP.3
inherited widgetが呼び出され、子widgetに通知する
ただし、inheritFromWidgetOfExactTypeでビルドした子widgetのみ通知がくる
STEP.4
カウント数を表示するテキストがリビルド
順を追って、説明していきます。

STEP1:ボタン押すと、setStateが呼び出される

WidgetIncrementBtn
class WidgetIncrementBtn extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final HomePageState state = HomePage.of(context, rebuild: false);
    return FloatingActionButton(
      onPressed: () => state._incrementCounter(),
      tooltip: 'Increment',
      child: Icon(Icons.add),
    );
  }
}
ボタンを押すと、final HomePageState state = HomePage.of(context, rebuild: false);が最初に呼びされます。ボタンの見た目は何も変わらなくてOKなので、引数rebuild: falseにすることで、ボタン自体をリビルドしないようにしてます。

関数HomePage.ofはこちらです。

HomePage
class HomePage extends StatefulWidget {
  const HomePage({Key key, this.child}) : super(key: key);

  final Widget child;

  @override
  HomePageState createState() => HomePageState();

  static HomePageState of(BuildContext context, {bool rebuild = true}) {
    return rebuild
        ? (context.inheritFromWidgetOfExactType(_InheritedWidget)
                as _InheritedWidget)
            .data
        : (context
                .ancestorInheritedElementForWidgetOfExactType(_InheritedWidget)
                .widget as _InheritedWidget)
            .data;
  }
}

inheritFromWidgetOfExactTypeを使用してビルドすると、自動的にStateの変化が通知されるようになります。一方で、ancestorInheritedElementForWidgetOfExactTypeを使うと、通知がいかないようになります。

カウントアップ用のボタンは見た目の変化はないので、リビルドの必要がありません。そのため、引数rebuild: falseを渡しています。

そして、_incrementCounter関数が呼び出され、数字がカウントアップします。

STEP.2:StatefulWidgetがリビルド

STEP1でsetStateが呼びされ、StatefulWidgetがリビルドされます。

HomePageState
class HomePageState extends State {
  int counter = 0;

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

  @override
  Widget build(BuildContext context) {
    return _InheritedWidget(
      child: widget.child,
      data: this,
    );
  }
}

このとき、_InheritedWidgetに子widgetHomePageState自体のデータを渡します。

_InheritedWidget
class _InheritedWidget extends InheritedWidget {
  _InheritedWidget({
    Key key,
    Widget child,
    this.data,
  }) : super(key: key, child: child);

  final HomePageState data;

  @override
  bool updateShouldNotify(InheritedWidget oldWidget) => true;
}

STEP.3:inherited widgetが呼び出され、子widgetに通知する

ここでStateに変化があると、bool updateShouldNotify(InheritedWidget oldWidget) => true;が発火します。

そして、HomePageState of(BuildContext)があるWidgetに通知がいきます。

このサンプルでは、カウントアップされた数字を表示するWidgetNumTextに通知がいきます。

WidgetNumText
class WidgetNumText extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final HomePageState state = HomePage.of(context);
    return Text(
      '${state.counter}',
      style: Theme.of(context).textTheme.display1,
    );
  }
}

STEP.4:カウント数を表示するテキストがリビルド

そして、WidgetNumTextがリビルドされ、カウントがアップされた数字が表示されます!

最後に

以上になります。これからInheritedWidgetを学ぶ方の参考になれば幸いです。

また下記2つのリンクが凄く丁寧でわかりやすかったので、ぜひ一度目を通してみると良いと思います!
参考 QiitaInheritedWidget/InheritedModelとは何か 参考 InheritedWidget を完全に理解するMedium

コメントを残す