Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use a riverpod family provider without passing parameter

Is there a way to access an instance of a provided ".family" change notifier (which has already been instantiated, passing the right parameters) without passing the parameters again?

IN PROVIDER

When you create a provider of a ChangeNotifier (which requires parameters), you can get the same change notifier it's providing with Provider.of<ChangeNotif>(context);

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

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

Once the provider has been created with its right params, you can get whatever it's providing anywhere down that widget tree without any syntax like Provider.of<ChangeNotif("dash")>(context) but rather Provider.of<ChangeNotif>(context).

IN RIVERPOD

Since you have to pass the parameters to the provider when getting an instance of it, I've had to assign the provider to a variable in order to pass it down to its children which need the change notifier the provider is providing.

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),
     ]
    );
  }
}

Is there a way to get the instantiated _changeNotif without passing it as a parameter to a child widget? Is there any way to get the same instance of _changeNotif in another widget that isn't a child of A (like using Provider.of<ChangeNotif>(context) in Provider without having to pass new parameters)?

like image 646
Prince Avatar asked Nov 19 '20 18:11

Prince


People also ask

What is a riverpod provider?

As we've seen, Riverpod providers are global but their state isn't. The state of a provider is stored inside a ProviderContainer, an object that is implicitly created by ProviderScope. This means that separate widget tests will never share any state. So there is no need for setUp () and tearDown () methods.

How do I use the select() method in riverpod?

The select () method is available on all Riverpod providers and can be used whenever we call ref.watch () or ref.listen (). For more info, see Using "select" to filter rebuilds in the Riverpod docs. In the examples above, we have used ref.watch () every time we needed to get the value of a provider inside a build () method.

How do I enable logging for the entire riverpod app?

And Riverpod includes a ProviderObserver that we can subclass to implement a Logger: This gives us access to both the previous and new value. We can enable logging for the entire app by adding the Logger to the list of observers inside the ProviderScope: To improve the output of the logger, we can add a name to our providers:

What is riverpod in flutter?

Riverpod is a popular Flutter state management library that shares many of the advantages of Provider and brings many additional benefits. Riverpod is a complete rewrite of the Provider package to make improvements that would be otherwise impossible. I won't lie: Riverpod comes with a bit of a learning curve.


1 Answers

I use this extension to get all the instances for StateNotifierProviderFamily instances, but you could also do this for ChangeNotifierProviderFamily. You do need to register them in the provider:

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 {};
});

You can register like this:

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

Of course, you also need to import the extension for this to work.

For the ChangeNotifierProviderFamily I think it would be something like this (I have not tested this):

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 {};
});
like image 93
TmKVU Avatar answered Sep 27 '22 18:09

TmKVU