avoid_ref_watch_outside_build
v0.8.0 Warning Riverpod State
This rule flags a subscribing provider read outside a build() method — for example in initState, dispose, a helper method, or an event handler callback.
It covers both ecosystems, because the mistake is identical in each:
Subscribing (flagged outside build) |
One-off (correct outside build) |
|
|---|---|---|
| Riverpod | ref.watch(...) |
ref.read(...) |
| package:provider | context.watch<T>() |
context.read<T>() |
Why use this rule
Section titled “Why use this rule”A subscribing read ties a listener to the lifecycle of a build. Called anywhere else it creates a subscription the framework does not manage: it can fire after the widget is gone, rebuild at surprising moments, or simply leak.
With package:provider this is not a smell but a crash — context.watch<T>() throws outright when called from initState, so the rule catches a runtime failure rather than a style problem.
The correct alternative depends on intent. For a one-off read in a callback, use ref.read / context.read<T>(). To react to provider changes with a side effect (navigation, a snackbar), use ref.listen inside build.
This rule is the counterpart to avoid_ref_read_inside_build: together they express the rule that subscribing belongs in build and reading once belongs outside it.
The two ecosystems are told apart by the receiver’s resolved type, not by its name — see the sibling rule for what that buys.
See also: Riverpod refs, provider: read vs watch
// Riverpodclass MyState extends ConsumerState<MyWidget> { @override void initState() { super.initState(); // Subscription created outside a build — leaks and misbehaves final value = ref.watch(someProvider); }
void onButtonTap() { // Also wrong: a callback should not subscribe final value = ref.watch(someProvider); }
@override Widget build(BuildContext context) => const SizedBox();}class MyState extends ConsumerState<MyWidget> { void onButtonTap() { // One-off read in a callback final value = ref.read(someProvider); }
@override Widget build(BuildContext context) { // Subscribe here — this is what watch is for final value = ref.watch(someProvider);
// React to changes with a side effect ref.listen(someProvider, (previous, next) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Changed to $next')), ); });
return Text(value); }}Configuration
Section titled “Configuration”This rule is in the recommended preset, so it is on with
preset: recommended or preset: opinionated. Add it to preset: core with
avoid_ref_watch_outside_build: true.
To turn it off:
rules: avoid_ref_watch_outside_build: falseTo keep the rule on but skip certain paths, use per-rule exclude.
Related rules
Section titled “Related rules”avoid_ref_read_inside_build— Subscribe in build; do not read once.avoid_ref_inside_state_dispose— Avoid accessing ref inside the dispose method of a ConsumerState.avoid_build_context_in_providers— Providers outlive widgets, so they should not receive a BuildContext.notifier_build— Classes annotated with @riverpod must define a build method.