Skip to content

use_setstate_synchronously

v1.0.0WarningConfigurableAsync Safety

This rule flags setState called after an await with no mounted guard in between.

This is a real crash, not a style preference. Between the await and the line after it the widget can be disposed — the user navigated back, a parent rebuilt without this child, a list item scrolled out of a ListView. Calling setState on a disposed State throws setState() called after dispose(), and it does so only under the timing that makes it hard to reproduce and easy to ship.

This is the State counterpart to use_ref_read_synchronously, and it shares that rule’s async-gap machinery. It applies inside any State subclass, including additional bases named through state_base_classes.

This rule is in the recommended preset.

Future<void> load() async {
final data = await repository.fetch();
setState(() => _data = data); // the widget may be gone
}
Future<void> load() async {
final data = await repository.fetch();
if (!mounted) return;
setState(() => _data = data);
}

The wrapper form is accepted too, for when there is nothing to do after the guard and an early return would only be noise:

Future<void> load() async {
final data = await repository.fetch();
if (mounted) setState(() => _data = data);
}

A guard is recognised when reaching the setState proves mounted:

Guard Recognised Why
if (!mounted) return; yes The lines below run only while mounted.
if (!mounted || failed) return; yes The return fires whenever mounted is false.
if (mounted) setState(...) yes The branch cannot be entered while unmounted.
if (mounted && ready) setState(...) yes Entering still requires mounted.
if (!mounted && failed) return; no Falls through while unmounted when failed is false.
if (mounted || ready) setState(...) no The branch also runs while unmounted.
if (mounted) { await x(); setState(...); } no The await opens a fresh gap inside the guard.
if (mounted) {} else { setState(...); } no The else runs precisely when the guard failed.

This rule is on with preset: recommended or preset: opinionated.

analysis_options.yaml
many_lints:
rules:
use_setstate_synchronously:
state_base_classes: []
Option Type Default Description
state_base_classes list [] Additional base classes to treat as a State

To disable this rule:

many_lints.yaml
rules:
use_setstate_synchronously: false

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