2020/12/19 :Consumer classの訳

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

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です