Skip to content

avoid_state_constructors

v0.4.0WarningFixConfigurableState Management

Flags State subclasses that have constructors with non-empty bodies or initializer lists. Initialization logic in State classes should live in initState(), not in the constructor, to respect the Flutter widget lifecycle.

The State constructor runs before the framework has fully initialized the state object. At construction time, widget, context, and other framework-provided properties are not yet available. Placing logic in the constructor can lead to subtle bugs when that logic depends on the widget tree. Using initState() ensures all framework wiring is in place.

See also: State class | State.initState | Dart lint: no_logic_in_create_state

A State doing setup in its constructor body. The constructor runs before the framework has attached widget or context, so anything that needs them is either unavailable or wrong:

class _SearchPageState extends State<SearchPage> {
late final TextEditingController _controller;
_SearchPageState() {
_controller = TextEditingController();
}
@override
Widget build(BuildContext context) => TextField(controller: _controller);
}

An initializer list is reported for the same reason — the value is decided before widget exists, so it can never depend on it:

class _CounterState extends State<Counter> {
final int _start;
_CounterState() : _start = 0;
@override
Widget build(BuildContext context) => Text('$_start');
}

Do the setup in initState, where widget is available:

class _SearchPageState extends State<SearchPage> {
late final TextEditingController _controller;
@override
void initState() {
super.initState();
_controller = TextEditingController(text: widget.initialQuery);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) => TextField(controller: _controller);
}

A field initializer that does not touch widget or context needs no constructor at all:

class _CounterState extends State<Counter> {
int _count = 0;
@override
Widget build(BuildContext context) => Text('$_count');
}

Only a constructor with a non-empty body or an initializer list is reported. An empty declared constructor is fine:

class _CounterState extends State<Counter> {
_CounterState();
int _count = 0;
@override
Widget build(BuildContext context) => Text('$_count');
}

Field initializers written on the field itself are not constructor code and are never reported, even when they run at the same moment.

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

To turn it off:

many_lints.yaml
rules:
avoid_state_constructors: 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_state_constructors:
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