Как использовать семейный провайдер Riverpod без передачи параметра

Есть ли способ получить доступ к экземпляру предоставленного .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, не передавая его в качестве параметра дочернему виджету? Есть ли способ получить тот же экземпляр _changeNotif в другом виджете, который не является дочерним элементом A (например, использование Provider.of<ChangeNotif>(context) в Provider без необходимости передавать новые параметры)?


person Prince    schedule 19.11.2020    source источник


Ответы (2)


Ознакомьтесь с примерами Riverpod, показывающими, как он решает эту проблему Пример Marvel

Преимущество использования семейства riverpo по сравнению с пакетом Provider заключается в том, что поставщик не позволяет вам читать несколько экземпляров одного и того же класса в дереве виджетов, а семейство riverpod не имеет этого ограничения.

в Провайдере

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()
);

Затем в MyWidget при выполнении Provider.of<ChangeNotif>(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);
  }
}
person EdwynZN    schedule 20.11.2020
comment
Это не ответило на вопрос. - person Scorb; 30.04.2021

Я использую это расширение, чтобы получить все экземпляры для 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 {};
});
person TmKVU    schedule 23.06.2021