Reputation: 53
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
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