Riverpod: Recompute a provider providing a list of items on receiving updates

Viewed 918

I have a simple use case where I am trying to recompute a provider providing a list of items on watching an item provider as follows :

final itemProvider = StateProvider<Item>((ref) {
    return Item() ;
});

final itemListProvider = Provider<List<Item>>((ref) {
 watch(itemProvider);
...//do something to compute new list
 return computedList ;
});

I want the itemListProvider to recompute automatically when itemProvider's state is changed from outside so that the itemListProvider keeps on adding the new items to the existing list it provides. Is there a way to achieve this in the above particular fashion using Riverpod? I know I could use a StateNotifierProvider or StateProvider and update the itemList using a state assignment call but I am looking forward to do it reactively.

Any help or guidance is appreciated. Thanks!

2 Answers

This can be accomplished handily with a single StateNotifierProvider.

class ItemList extends StateNotifier<List<Item>> {
  ItemList() : super([]);

  static final provider = StateNotifierProvider<ItemList, List<Item>>((ref) => ItemList());

  Item _current = Item();
  Item get current => _current;

  void addItem(Item item) {
    _current = item;
    state = [...state, item];
  }
}

Usage:

// Add an item, which updates the current item
watch(ItemList.provider.notifier).addItem(item);

// Get the current item
final currentItem = watch(ItemList.provider.notifier).current;

// Get the List<Item> from state
final itemList = watch(ItemList.provider);

If you really want to avoid StateNotifierProvider, you could do the following, but I would highly recommend you to use the first approach. Anyways:

final itemProvider = StateProvider<Item>((ref) => Item());

final itemListProvider = StateProvider<List<Item>>((ref) => []);

final computedListProvider = Provider<List<Item>>((ref) {
  final item = ref.watch(itemProvider);
  final itemList = ref.watch(itemListProvider);
  WidgetsBinding.instance?.addPostFrameCallback((_) {
    if (itemList.state.contains(item.state)) return;
    itemList.state = [...itemList.state, item.state];
  });

  return itemList.state;
});

Best approaches are

  1. For any input parameter that should be evaluated as part of the provider you can use riverPod .family. Link here (https://riverpod.dev/docs/concepts/modifiers/family/)

  2. Alternatively as per documentation itemListProvider should get re-evaluated if itemProvider is updated. Incase it is not for your usecase then in your widget you can call ref.refresh to re-evaluate the provider. For Refresh perform like below and change your provider to FutureProvider

    Future _refresh(){ ref.refresh(itemListProvider); return ref.read(itemListProvider.future); }

Related