Reputation: 118
I'm busy implementing a simple counter using riverpod. From the examples I've seen, the riverpod state is updated on one widget (by doing something like ref.read(counterProvider.notifier).increment()
) and changes to state are watched on the same widget. When I try this it works as expected, using ref.watch(counterProvider)
in the build
method of the same widget. The counter is updated on the UI.
My problem comes in when I update the counter on one widget, and watch for changes to the counter state on another widget (these widgets are two different tabs in a TabView). So basically my increment button is on one tab, and the value of the counter is displayed on the next tab. In this scenario, the counter doesn't get updated.
The weird thing is, if I add a ref.watch
on the same widget where I do the state update, the ref.watch
on the second tab also starts working and updates the UI correctly on that tab.
Anyone know why this might be happening? Both of these widgets are ConsumerStatefulWidget
s and my ProviderScope
is at the root of my app. I'm using riverpod V2, with the code generation.
My provider:
part 'counter.provider.g.dart';
@riverpod
class Counter extends _$Counter {
@override
int build() {
return 2;
}
void increment() {
state++;
}
}
First widget (state update):
ref.read(counterProvider.notifier).increment();
Second widget (state reading):
var x = ref.watch(counterProvider);
Upvotes: 0
Views: 3104
Reputation: 113
Since this is the first search result for "riverpod ref.watch not working", I thought I would share this tricky part of using NotifierProvider
that I couldn't find elsewhere.
I made a notifier provider method updateMyClass(...)
that I used in a button onPressed
callback like this:
ref.read(myClassNotifierProvider.notifier).updateMyClass(...);
But the other Widget wasn't refreshing. It was trying to use another notifier provider method I made called getMyClass()
to get the value and update when the value changed:
ref.read(myClassNotifierProvider.notifier).getMyClass();
No dice. Why? Should that ref.read
instead be ref.watch
? Nope, makes no difference.
The issue is solved by the following bit of code called somewhere in the Widget's build method (probably at the beginning):
ref.watch(myClassNotifierProvider);
See it? That lack of the .notifier
call? That is what Riverpod uses to know it should keep an eye on this Widget and refresh if needed when the value changes. Of course you also need to set the state
in the updateMyClass(...)
method to trigger the update too (code below).
I was assuming that calling the ref.watch
with the .notifier
call was the same as calling it without the .notifier
call. Not sure why it works this way, but it does kind of make sense; watching the .notifier
is maybe only looking at the basic class behind the values. Probably wouldn't want to refresh the Widget for every single method call. I guess the non-notifier ref.watch
can actually see the values, and tell the Widget to rebuild since the value changed.
import 'package:riverpod_annotation/riverpod_annotation.dart';
// Don't forget this part :)
part 'my_class_provider.g.dart';
class MyClass {
const MyClass(this.valueToUpdate);
final int valueToUpdate;
}
@riverpod
class MyClassNotifier extends _$MyClassNotifier {
@override
MyClass build() => const MyClass(42);
void updateMyClass(int newValue) => state = MyClass(newValue);
MyClass getMyClass() => state;
}
Upvotes: 0
Reputation: 1241
Providers annotated with @riverpod
will be disposed automatically when it is not being watched or listened. It seems like your counterProvider
is getting disposed when switching to another tab.
You can add a print statement to verify that.
@override
int build() {
ref.onDispose(() {
print('disposed');
});
return 2;
}
Upvotes: 2