avoid_state_constructors
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.
Why use this rule
Section titled “Why use this rule”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');}Known limitations
Section titled “Known limitations”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.
Turning this rule off
Section titled “Turning this rule off”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:
rules: avoid_state_constructors: falseTo keep the rule on but skip certain paths, use per-rule exclude.
Options
Section titled “Options”Projects with a state abstraction that does not extend Flutter’s State can
opt that base class into this rule:
many_lints: rules: avoid_state_constructors: state_base_classes: [AppState]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 |
Related rules
Section titled “Related rules”prefer_immutable_state— Ensure classes named as state are annotated with @immutable.avoid_empty_setstate— Don’t call setState with an empty callback.avoid_inherited_widget_in_initstate— Don’t look up inherited widgets inside initState.avoid_late_context— Don’t read BuildContext in a late field initializer.