どーも、ぐるたか@guru_takaです。
ボタンを押すと、数字がカウントされていくサンプルをInherited Widget
で書き直しました。
setState
が呼び出されると、必要なWidget
のみ、ビルドされるようになっています!
ここでは、サンプルに沿って、Inherited Widget
の使い方を紹介します。サンプルソースは下記リンクにありますので、よろしければご覧ください。
参考
gurutaka/inherited_widget_sampleGithub
目次
InheritedWidgetとは?
InheritedWidget
は、ツリーの配下に対し、効率的に情報を伝えるWidgetのクラスです。下の例が最もわかりやすいので、引用します。
すごく簡単な図ですが、Cボタンを押下したら、Stateが変更されて、Aのテキストだけがリビルドされて表示が変わる。
このように、InheritedWidget
を使うと、効率的に情報をツリー下に渡せます!
処理の主な流れ
今回のサンプルの処理の流れに下にまとめてみました。
setState
が呼び出されるStatefulWidget
がリビルドinherited widget
が呼び出され、子widget
に通知するinheritFromWidgetOfExactType
でビルドした子widget
のみ通知がくるSTEP1:ボタン押すと、setState
が呼び出される
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
はこちらです。
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
がリビルドされます。
class HomePageState extends State {
int counter = 0;
void _incrementCounter() {
setState(() {
counter++;
});
}
@override
Widget build(BuildContext context) {
return _InheritedWidget(
child: widget.child,
data: this,
);
}
}
このとき、_InheritedWidget
に子widget
とHomePageState
自体のデータを渡します。
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
に通知がいきます。
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
コメントを残す