Skip to content

use_ref_read_synchronously

v0.4.0 Warning Fix Async Safety

This rule catches ref.read() calls that happen after an await inside async callbacks within a ConsumerWidget or ConsumerState build method, without a mounted check first. If the widget is unmounted while the async operation runs, ref.read may return stale or invalid data.

Async callbacks like onPressed: () async { ... } can easily outlive the widget that created them. After an await, the widget might already be disposed. Calling ref.read at that point reads from a potentially dead reference. In ConsumerWidget callbacks, use context.mounted as the guard (for Notifier methods, the sibling rule use_ref_and_state_synchronously checks for ref.mounted instead).

See also: Riverpod documentation | Dart lint: use_build_context_synchronously

class MyWidget extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
return ElevatedButton(
onPressed: () async {
await Future<void>.delayed(const Duration(seconds: 1));
// Widget may be unmounted — ref.read is unsafe
ref.read(someProvider);
},
child: const Text('Tap'),
);
}
}
class MyWidget extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
return ElevatedButton(
onPressed: () async {
await Future<void>.delayed(const Duration(seconds: 1));
if (!context.mounted) return;
ref.read(someProvider);
},
child: const Text('Tap'),
);
}
}

This rule is in the opinionated preset, so it is on with preset: opinionated, or by name:

many_lints.yaml
rules:
use_ref_read_synchronously: true

To turn it off again:

many_lints.yaml
rules:
use_ref_read_synchronously: false

To keep the rule on but skip certain paths, use per-rule exclude.