I keep mixing ref.watch and ref.read and sometimes get a warning or a screen that never updates. What is the rule for each, and where is it safe to call them?
DEV3 Developer Forum
In Riverpod, when should I use ref.watch, ref.read and ref.listen?
Ask a question
Every post is reviewed first. Usually reviewed within 24 hours.
dev3 ·
0
1 reply
dev3 · Accepted answer
The rule
ref.watch(provider): use inbuild(and inside other providers). It returns the value and rebuilds the widget or provider whenever the value changes.ref.read(provider): use in callbacks (onPressed,onTap,initState-like code). It reads once, never subscribes.ref.listen(provider, (prev, next) {...}): use for side effects that are not UI, such as showing a SnackBar, navigating or logging when state changes.
class LoginButton extends ConsumerWidget {
const LoginButton({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final state = ref.watch(loginControllerProvider);
ref.listen(loginControllerProvider, (prev, next) {
if (next.hasError) {
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text('${next.error}')));
}
});
return FilledButton(
onPressed: state.isLoading
? null
: () => ref.read(loginControllerProvider.notifier).submit(),
child: const Text('Sign in'),
);
}
}
Why not read in build
ref.read in build gives a stale snapshot: the widget will not update when the provider changes. It looks fine until the first state change.
Why not watch in callbacks
Watching in an event handler is flagged (it is not a valid place for a subscription) and is wasted work.
Inside a Notifier
In build() of a Notifier/AsyncNotifier use ref.watch for dependencies you want to react to. In its other methods use ref.read.
Reduce rebuilds
ref.watch(userProvider.select((u) => u.name)) rebuilds only when name changes.
Pitfall
Never call ref.read to get a value you then store in a field of a long-lived object, it will not follow later changes.
0