neonx97
neonx97

Reputation: 53

How to modify the state of a StateNotifier Provider from another provider in Riverpod?

Im very new to flutter and riverpod so im very sorry if the question is not clear, let me know if this is the case

I have 2 providers, one listens to real-time changes of a FireStore collection, and the other one is a List Service to handle the actual list of items being received from FireStore. I want to add the list received to the itemList and then notify the widgets listening to the itemListProvider so that the listview can be rebuilt

Item List state notifier provider

final itemListProvider =
    StateNotifierProvider<ItemListService, List<Item>>(
        (ref) => ItemListService([]));

realtime listener

final firestoreListUpdateWatcher = Provider((ref) {
  //Listens to a stream which provide query snapshot, this works as intended
  final firestoreListWatcher = ref.watch(firestoreListProvider);
  
  //using `ref.watch(itemListProvider.notifier)` because i want to be able 
  //to change the state from this provider
  final itemListProvider = ref.watch(itemListProvider.notifier);
  
  firestoreLisUpdateWatcher.when(
      data: (value) {
          for (int i = 0; i < value.docChanges.length; i++) {
            QueryDocumentSnapshot currentQuery = value.docs[i];
            //current item in list
            ListItem item = fireStoreList.fromMap(currentQuery.data());

            //add current item to the ListService 
            itemListProvider.add(item);
          }
      },
      loading: () {},
      error: (err, stack) {});
});

Item List Service class

class ItemListService extends StateNotifier<List<Item>> {

  ItemListService(List<Item> itemList):super(itemListList ?? []);

  void add(Item it) {
    state = [...state, it];
  }

  //more methods modifying the list....
}

In my widget's build function i simply want to listen to the changes to the itemListProvider and recreate the list based on the current state

This is not working, my widget does not get rebuilt when a change to the list is made. I believe the reason it is not working is because when i create the "watcher" in my "firestoreListUpdateWatcher" using ref.watch(itemListProvider.notifier) im creating a new instance which my widget is not listening to and thus does not get notified when changes happen. Is this assumption correct?

Also, how can i achieve this behavior (modifying the list from another provider)

Is there a better way of designing my app to avoid this constraint?

Thanks in advance for your help

Upvotes: 5

Views: 5207

Answers (1)

EdwynZN
EdwynZN

Reputation: 5601

Exactly as you said, ref.watch is recreating the provider, thats why you're not seeing your changes, something you could do with your current logic is to watch the stream instead of the provider and updates the state with a listener in the same itemListProvider without creating another Provider:

/// This is your StateNotifierProvider
final itemListProvider = StateNotifierProvider.autoDispose<ItemListService, List<Item>>((ref) {
  //Listen to the stream itself instead of the value hold by the provider
  final firestoreListStream = ref.watch(firestoreListProvider.stream);
  
  //Create the instance of your StateNotifier
  final itemListProvider = ItemListService([]);

  /// subscribe to the stream to change the state accordingly
  final subscription = firestoreListStream.listen((value) {
          for (int i = 0; i < value.docChanges.length; i++) {
            QueryDocumentSnapshot currentQuery = value.docs[i];
            //current item in list
            ListItem item = fireStoreList.fromMap(currentQuery.data());

            //add current item to the ListService 
            itemListProvider.add(item);
          }
      });
  /// When you stop using this porvider cancel the subscription to the stream, to avoid memory leaks
  ref.onDispose(subscription.cancel);
  return itemListProvider;
});

With this you won't need another Provider (firestoreListUpdateWatcher)

As a recomendation I note that you iterate through a for loop and add them individually, which will force the state to update multiple times, I would recommend get the list first and then update the state with an addAll method

final subscription = firestoreListStream.listen((value) {
   List<ListItem> items = [];
   for (int i = 0; i < value.docChanges.length; i++) {
       QueryDocumentSnapshot currentQuery = value.docs[i];
       //current item in list
       ListItem item = fireStoreList.fromMap(currentQuery.data());

       //add current item to the ListService 
       items.add(item);
   }
   ItemListService.addAll(items);
          
});

And in your ItemsService create addAll method

void addAll(Iterable<Item> it) {
  state = [...state, ...it];
}

Upvotes: 4

Related Questions