use_setstate_synchronously
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);}Which guards count
Section titled “Which guards count”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. |
Configuration
Section titled “Configuration”This rule is on with preset: recommended or preset: opinionated.
Options
Section titled “Options”many_lints: rules: use_setstate_synchronously: state_base_classes: []rules: use_setstate_synchronously: state_base_classes: []| Option | Type | Default | Description |
|---|---|---|---|
state_base_classes |
list | [] |
Additional base classes to treat as a State |
Turning this rule off
Section titled “Turning this rule off”To disable this rule:
rules: use_setstate_synchronously: falseTo keep the rule on but skip certain paths, use per-rule exclude.
Related rules
Section titled “Related rules”use_ref_and_state_synchronously— Check ref.mounted before using ref or state after an await.use_ref_read_synchronously— Add a mounted guard before calling ref.read after an await.check_is_not_closed_after_async_gap— Check isClosed before emitting state after an await.require_atomic_async_updates— Re-read shared state after an await instead of writing back a stale value.