use-ref-and-state-synchronously

Path
lint/correctness/use-ref-and-state-synchronously
Default
On (recommended)
Origin
Falcon
Domain
riverpod
Rule
499 of 512
recommendedriverpod

Flags notifier ref and state accesses after an async gap without ref.mounted.

A notifier may be disposed while its asynchronous method is suspended. Guard subsequent accesses with ref.mounted. Only Riverpod 3 AnyNotifier-declared ref and state members are checked; older APIs without ref.mounted, shadowed locals, unrelated classes, and mounted checks themselves are excluded. Nested function bodies form independent async boundaries.

Invalid

example.dartdart
import 'package:flutter_riverpod/flutter_riverpod.dart';
final provider = Object();
Future<void> pause() async {}
abstract class Count extends Notifier<int> {
  Future<void> increment() async {
    await pause();
    state += 1;
  }
  Future<void> explicitThis() async {
    await pause();
    this.state = 2;
  }
  Future<void> read() async {
    await pause();
    ref.read(provider);
  }
  Future<void> staleGuard() async {
    if (!ref.mounted) return;
    await pause();
    state = 3;
  }
  Future<void> secondGap() async {
    await pause();
    if (!ref.mounted) return;
    await pause();
    state = 4;
  }
}
abstract class AsyncCount extends AsyncNotifier<int> {
  Future<void> increment() async {
    await pause();
    state = 5;
  }
}
abstract class StreamCount extends StreamNotifier<int> {
  Future<void> increment() async {
    await pause();
    state = 6;
  }
}
Check `ref.mounted` before using notifier `ref` or `state` after an async gap.
6 await pause();
7 state += 1;
Check `ref.mounted` before using notifier `ref` or `state` after an async gap.
10 await pause();
11 this.state = 2;
Check `ref.mounted` before using notifier `ref` or `state` after an async gap.
14 await pause();
15 ref.read(provider);
Check `ref.mounted` before using notifier `ref` or `state` after an async gap.
19 await pause();
20 state = 3;
Check `ref.mounted` before using notifier `ref` or `state` after an async gap.
25 await pause();
26 state = 4;
Check `ref.mounted` before using notifier `ref` or `state` after an async gap.
31 await pause();
32 state = 5;
Check `ref.mounted` before using notifier `ref` or `state` after an async gap.
37 await pause();
38 state = 6;

Valid

example.dartdart
import 'package:flutter_riverpod/flutter_riverpod.dart';
final provider = Object();
Future<void> pause() async {}
abstract class Count extends Notifier<int> {
  Future<void> beforeAwait() async {
    state += 1;
    await pause();
  }
  Future<void> guarded() async {
    await pause();
    if (ref.mounted) state += 1;
  }
  Future<void> earlyReturn() async {
    await pause();
    if (!ref.mounted) return;
    state += 1;
    ref.read(provider);
  }
  Future<void> explicitGuard() async {
    await pause();
    if (!this.ref.mounted) return;
    this.state = 1;
  }
  Future<void> shortCircuit() async {
    await pause();
    if (ref.mounted && state > 0) state = 2;
  }
  Future<void> shadowed() async {
    int state = 1;
    await pause();
    state += 1;
  }
  Future<void> nested() async {
    await pause();
    void callback() { state = 1; }
    callback();
  }
  Future<void> parenthesizedGuard() async {
    await pause();
    if (!(ref).mounted) return;
    state = 1;
  }
}
class Unrelated {
  int state = 0;
  Future<void> update() async {
    await pause();
    state += 1;
  }
}
abstract class OlderNotifier extends BuildlessNotifier<int> {
  Future<void> update() async {
    await pause();
    state = 1;
  }
}
const alive = true;
abstract class ConstantGuard extends Notifier<int> {
  Future<void> update() async {
    await pause();
    if (ref.mounted == alive) state = 1;
  }
  Future<void> parenthesized() async {
    await pause();
    if (ref.mounted == (alive)) state = 1;
  }
  Future<void> nestedParenthesized() async {
    await pause();
    if (ref.mounted == ((alive))) state = 1;
  }
}

Configure use-ref-and-state-synchronously

Set its severity in falcon.json. See the configuration guide.

falcon.jsonjson
{
  "linter": {
    "rules": {
      "correctness": {
        "use-ref-and-state-synchronously": "error"
      }
    }
  }
}

Or silence one occurrence with a suppression comment; the reason is required.

example.dartdart
// falcon-ignore lint/correctness/use-ref-and-state-synchronously: <reason>
  • use-ref-read-synchronouslyrecommendedriverpod

    Flags Riverpod WidgetRef.read calls after an async gap without a mounted guard.

  • use-setstate-synchronouslyrecommendedflutter

    Require a mounted check before calling setState after an async gap.

  • valid-regexpsvalid_regexpsrecommended

    Disallow statically known invalid dart:core regular expressions.

  • void-checksvoid_checksrecommended

    Don't pass or assign values to void (or FutureOr<void>) locations.

  • add-equatable-propsrecommendedequatable

    Flags concrete Equatable subclasses that do not declare an instance props property.

  • always-pass-global-keyflutter

    Keep GlobalKeys stable and pass owned State fields to widgets.

  • always-remove-listenerrecommendedflutter

    Requires Flutter Listenable registrations in State to be removed.

  • always-use-package-importsalways_use_package_imports

    Disallow relative imports in libraries under a package's lib/ directory.