Skip to content

avoid_mounted_in_setstate

v0.4.0WarningConfigurableState Management

Warns when mounted or context.mounted is checked inside a setState callback. If the widget has been disposed, setState itself throws an exception before the callback ever runs, making any mounted check inside it useless.

A common misconception is that checking mounted inside setState protects against calling setState on a disposed widget. In reality, setState validates the state object immediately when called — if the widget is unmounted, it throws before executing the callback. The mounted check must happen before the setState call, not inside it.

See also: State.mounted | State.setState

An async handler that guards inside the callback. The guard never runs — setState has already thrown by then — and the state still gets assigned when the widget survives, so the code looks like it works:

class _ProfilePageState extends State<ProfilePage> {
String _name = '';
Future<void> _load() async {
final name = await fetchName();
setState(() {
if (mounted) {
_name = name;
}
});
}
@override
Widget build(BuildContext context) => Text(_name);
}

context.mounted inside the callback is the same mistake:

class _ProfilePageState extends State<ProfilePage> {
String _name = '';
Future<void> _load() async {
final name = await fetchName();
setState(() {
if (context.mounted) {
_name = name;
}
});
}
@override
Widget build(BuildContext context) => Text(_name);
}

Guard before the call, and let the callback do nothing but mutate:

class _ProfilePageState extends State<ProfilePage> {
String _name = '';
Future<void> _load() async {
final name = await fetchName();
if (!mounted) return;
setState(() {
_name = name;
});
}
@override
Widget build(BuildContext context) => Text(_name);
}

When the value is needed for something that also uses the context — a snackbar, a route — context.mounted reads better, and it belongs in the same place:

class _ProfilePageState extends State<ProfilePage> {
String _name = '';
Future<void> _load() async {
final name = await fetchName();
if (!context.mounted) return;
setState(() {
_name = name;
});
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Welcome, $name')),
);
}
@override
Widget build(BuildContext context) => Text(_name);
}

The whole callback is searched, including nested closures. A mounted read inside a closure that setState merely stores for later — rare, but legal — is reported too.

Only an inline callback is inspected. setState(_applyName) passes a method reference, and the rule does not follow it.

This rule is in the recommended preset, so it is on with preset: recommended or preset: opinionated. Add it to preset: core with avoid_mounted_in_setstate: true.

To turn it off:

many_lints.yaml
rules:
avoid_mounted_in_setstate: false

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

Projects with a state abstraction that does not extend Flutter’s State can opt that base class into this rule:

analysis_options.yaml
many_lints:
rules:
avoid_mounted_in_setstate:
state_base_classes: [AppState]
Option Type Default Description
state_base_classes list of strings [] Additional non-State base classes whose subclasses should be treated as state classes