【问题标题】:How to use a riverpod family provider without passing parameter如何在不传递参数的情况下使用riverpod 系列提供程序
【发布时间】:2021-03-03 04:12:39
【问题描述】:

有没有办法在不再次传递参数的情况下访问提供的“.family”更改通知器(已经实例化,传递正确的参数)的实例?

供应商

当您创建 ChangeNotifier 的提供者(需要参数)时,您可以获得与 Provider.of<ChangeNotif>(context) 提供的相同的更改通知;

class ChangeNotif extends ChangeNotifier {
  final String id;
  const ChangeNotif(this.id);
}

ChangeNotifierProvider(create: (_) => ChangeNotif("dash"));

使用正确的参数创建提供程序后,您可以在该小部件树的任何位置获取它提供的任何内容,而无需像 Provider.of<ChangeNotif("dash")>(context) 之类的任何语法,而是 Provider.of<ChangeNotif>(context)

在河中

由于在获取它的实例时必须将参数传递给提供者,因此我必须将提供者分配给一个变量,以便将其传递给需要提供者提供的更改通知器的子代。

final changeNotifProvider = ChangeNotifierProvider.family<ChangeNotif, String>((ref, id) => ChangeNotif(id));

class A extends HookWidget {
  build() {
    final _changeNotifProvider = changeNotifProvider("dash");
    final _changeNotif = useProvider(_changeNotifProvider);

    return Column(
     children: [
       B(),
       c(_changeNotifProvider),
     ]
    );
  }
}

有没有办法获得实例化的_changeNotif 而无需将其作为参数传递给子小部件?有什么方法可以在另一个不是 A 子级的小部件中获取相同的 _changeNotif 实例(例如在 Provider 中使用 Provider.of&lt;ChangeNotif&gt;(context) 而无需传递新参数)?

【问题讨论】:

    标签: flutter flutter-provider state-management riverpod


    【解决方案1】:

    我使用此扩展来获取StateNotifierProviderFamily 实例的所有实例,但您也可以为ChangeNotifierProviderFamily 执行此操作。您确实需要在提供程序中注册它们:

    extension StateNotifierProviderFamilyExtension<
        Notifier extends StateNotifier<Value>,
        Value,
        Param> on StateNotifierProviderFamily<Notifier, Value, Param> {
      /// Get all the registered versions of this `StateNotifierProviderFamily`
      ///
      /// This function requires a reader to the current `ProviderScope` to
      /// get the `familyKeysStorageProvider`
      ///
      /// The family needs to be registered with the `familyKeysStorageProvider`,
      /// like this:
      ///
      /// ```dart
      /// final keyStorage = ref.read(familyKeysStorageProvider(myProvider));
      /// keyStorage.state.add(myKey);
      /// ```
      Iterable<T> all<T extends StateNotifierProvider<Notifier, Value>>(
          Reader read) {
        return read(_familyKeysStorageProvider(this))
            .state
            .map((key) => call(key) as T);
      }
    
      Set<dynamic> keys(Reader read) =>
          read(_familyKeysStorageProvider(this)).state;
      void addKey(Reader read, dynamic key) => this.keys(read).add(key);
    }
    
    /// Use this provider to register keys for a `StateNotifierProviderFamily`
    /// object.
    ///
    /// After registration of the keys, it is possible to retrieve all registered
    /// instances with the `all(Reader read)` method on `StateNotifierProviderFamily`
    final _familyKeysStorageProvider =
        StateProvider.family<Set<dynamic>, StateNotifierProviderFamily>((ref, key) {
      return {};
    });
    

    你可以这样注册:

    final myStateProvider = StateNotifierProvider.family<
        MyStateNotifier,
        MyState,
        MyKey>(
      (ref, key) {
        myStateProvider.addKey(ref.read, key);
        return MyStateNotifier(ref.read, key);
      },
    );
    

    当然,您还需要导入扩展才能使其工作。

    对于ChangeNotifierProviderFamily,我认为应该是这样的(我还没有测试过):

    extension ChangeNotifierProviderFamilyExtension<Notifier extends ChangeNotifier,
        Param> on ChangeNotifierProviderFamily<Notifier, Param> {
      Iterable<T> all<T extends ChangeNotifierProvider<Notifier>>(Reader read) {
        return read(_notifierFamilyKeysStorageProvider(this))
            .state
            .map((key) => call(key) as T);
      }
    
      Set<dynamic> keys(Reader read) =>
          read(_notifierFamilyKeysStorageProvider(this)).state;
      void addKey(Reader read, dynamic key) => this.keys(read).add(key);
    }
    
    final _notifierFamilyKeysStorageProvider =
        StateProvider.family<Set<dynamic>, ChangeNotifierProviderFamily>(
            (ref, key) {
      return {};
    });
    

    【讨论】:

      【解决方案2】:

      查看riverpod示例,了解它如何解决该问题Marvel Example

      使用riverpo family vs Provider package的优势在于provider不允许你在widget树下读取同一个类的多个实例,riverpod family没有这个限制

      在提供者中

      MultiProvider(
         providers: [
            ChangeNotifierProvider<ChangeNotif>(
              create: (_) => ChangeNotif('dash')
            ),
            ChangeNotifierProvider<ChangeNotif>(
              create: (_) => ChangeNotif('dash2')
            ),
          // and many more of the same provider but different initial value
         ].
         child: MyWidget()
      );
      

      然后在MyWidgetProvider.of&lt;ChangeNotif&gt;(context) 时,你只会得到MultiProvider 创建的最后一个,无法区分相同类型的多个提供者,现在在riverpod 中解决了这个问题,并且传递家庭的方法是使用具有初始值的 ScopedProvider。

      final _idProvider = ScopedProvider<String>(null);
      
      class A extends HookWidget {
      
        @override
        Widget build() {
          final _changeNotif = useProvider(changeNotifProvider("dash"));
      
          return ProviderScope(
            overrides: [
               _idProvider .overrideWithValue("dash"),
            ],
            child: Column(
              children: [
                B(),
                c(),
              ]
            )
          );
        }
      }
      

      现在所有小部件(B 和 C)都可以读取用于创建族的参数,如果提供者已经存在,它将检索它而不重新创建它

      class A extends HookWidget {
      
        @override
        Widget build() {
          final id = useProvider(_idProvider);
          final _changeNotif = useProvider(changeNotifProvider(id)); //the ProviderContainer will check if there is already a family with that id and retrieve it
      
          return Text(_changeNotif.id);
        }
      }
      

      【讨论】:

      • 这没有回答问题。
      猜你喜欢
      • 2021-04-16
      • 2021-08-20
      • 1970-01-01
      • 2010-09-29
      • 2022-12-23
      • 2020-12-31
      • 2015-05-11
      • 2022-06-30
      • 2019-06-11
      相关资源
      最近更新 更多