Skip to content

avoid_empty_setstate

v0.8.0WarningConfigurableState Management

This rule flags setState(() {}) — a call whose callback contains no statements.

An empty callback means the state was already mutated somewhere else and setState is being used purely as a “please rebuild” signal. It works, but it separates the mutation from the rebuild request, which causes three problems:

  • Readers cannot see what changed by looking at the setState call.
  • The framework’s debug assertions, which run around the callback, no longer bracket the actual mutation.
  • If the mutation later moves after the setState call, the rebuild silently renders stale values.

Putting the mutation inside the callback fixes all three and costs nothing.

See also: State.setState docs

class Counter extends StatefulWidget {
const Counter({super.key});
@override
State<Counter> createState() => _CounterState();
}
class _CounterState extends State<Counter> {
int _count = 0;
void _increment() {
_count++;
// The rebuild request is detached from the change
setState(() {});
}
@override
Widget build(BuildContext context) => Text('$_count');
}
class Counter extends StatefulWidget {
const Counter({super.key});
@override
State<Counter> createState() => _CounterState();
}
class _CounterState extends State<Counter> {
int _count = 0;
void _increment() {
setState(() {
_count++;
});
}
@override
Widget build(BuildContext context) => Text('$_count');
}

Only a genuinely empty block is reported. A callback containing any statement is left alone, even if that statement has no effect — proving a statement is a no-op is outside this rule’s scope.

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

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

many_lints.yaml
rules:
avoid_empty_setstate: true

To turn it off again:

many_lints.yaml
rules:
avoid_empty_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_empty_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