Obtains Provider<T> from its ancestors and passes its value to builder.
先祖ウィジェットからProvider<T>を取得し、builderにその値を渡す。
そのまま訳しているが、取得するのはT型のはず。
Consumerウィジェットはファンシーな挙動はありません。新しいウィジェットの中でただProvider.ofメソッドを呼び出すだけです。そしてbuildメソッドの返り値としてbuilderフィールド(にセットされたコールバック)の返り値を返します。
builderはnullではいけません。そしてbuilderは何回も呼び出されます。(Provider.ofで取得した値が変化する度に)
Consumerウィジェットには二つの主な目的があります。
Providerの子孫ウィジェットのBuildContextを用いることができない場合でもProviderが提供する値(value)を取得できます。それゆえProvider.ofは使えません。
このシナリオは、以下のようなコードで発生します。
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => Foo(),
child: Text(Provider.of<Foo>(context).value),
);
}
5行目のcontextは2行目の引数のcontextなので、5行目の
Provider.of<Foo>(context)
は、このbuildメソッドを持つウィジェットの祖先ウィジェットからFoo型を探す。実際やりたいのは、3行目のChangeNotifierProviderが提供するFoo()を取得したい訳なので、意図とは違う挙動、ということになる。
このサンプルはProviderNotFoundExceptionをスローします。なぜなら、Provider.ofはProviderの祖先のBuildContextを引数に渡されて呼び出されているからです。
代わりに、Consumerウィジェットを使うことができます。Consumerを使えば、Provider.ofメソッドはProviderそのもののBuildContextと共に呼び出されるので、意図通りに取得したいFoo型の値を取得できます。
Consumerを使えば、前述のサンプルは下記のようになります。
@override Widget build(BuildContext context) { return ChangeNotifierProvider( create: (_) => Foo(), child: Consumer<Foo>( builder: (_, foo, __) => Text(foo.value), }, ); }
このサンプルではProviderNotFoundExceptionはスローされません。正しくTextがビルドされます。そしてfooが変更されるたびにTextがアップデートされます。
*より詳細な再構築を提供することにより、パフォーマンスの最適化に役立ちます。
[Provider.of]メソッドに’listen: false’が渡されない限り、「Provider.ofメソッドに渡されたBuildContextに関連づけられたウィジェット」
は、取得された値が変更される度リビルドされます。これは期待される振る舞いですが、しかし時には必要を超えるリビルドが行われる場合もあります。
以下にサンプルを示します。
/// ```dart /// @override /// Widget build(BuildContext context) { /// return FooWidget( /// child: BarWidget( /// bar: Provider.of<Bar>(context), /// ), /// ); /// } /// ```
上記のコードで、’BarWidget’だけが[Provider.of]で返される値に依存しています。しかし、`Barが変更された時、
`BarWidget`と共に`FooWidget`もリビルドされます。
よく分からないが、contextに対応するウィジェットが全てリビルドされる、ということか?
`BarWidget`だけがリビルドされるのが理想的です。これを実現するために[Consumer]を使います。
そのためには、providerに依存しているウィジェット(BarWidget)のみを[Consumer]でラップします↓。
/// ```dart /// @override /// Widget build(BuildContext context) { /// return FooWidget( /// child: Consumer<Bar>( /// builder: (_, bar, __) => BarWidget(bar: bar), /// ), /// ); /// } /// ```
こうすると、Bar
がアップデート(変更)されると、BarWidget
のみがリビルドされます。
もしproviderに依存しているのがFooWidget
の場合はどうでしょう。
以下にサンプルを示します。
/// ```dart /// @override /// Widget build(BuildContext context) { /// return FooWidget( /// foo: Provider.of<Foo>(context), /// child: BarWidget(), /// ); /// } /// ```[Consumer]を使ってください。このシナリオではオプション引数の` child `を使います↓。
/// ```dart /// @override /// Widget build(BuildContext context) { /// return Consumer<Foo>( /// builder: (_, foo, child) => FooWidget(foo: foo, child: child), /// child: BarWidget(), /// ); /// } /// ```
↑の例では、BarWidget
は[builder]の外側でビルドされます。それからBarWidget
は最後のパラメータとして[builder]に渡されます。
これはつまり、新しい値と共に[builder]が呼び出された時、BarWidget
の新しいインスタンスが生成されることはありません。
上記のようにすることで、Flutterに対して、BarWidget
をリビルドする必要がないことを知らせます。
ですから上記のようにすれば、Foo
が変化した時に、(BarWidget
はリビルドせずに)FooWidget
だけをリビルドさせることができます。
参考
https://pub.dev/documentation/provider/latest/provider/Consumer-class.html
consumer.dart