Skip to content

proper_super_calls

v0.4.0WarningFixConfigurableControl Flow

Warns when super lifecycle methods are called in the wrong order in State subclasses. Methods like initState, didUpdateWidget, activate, didChangeDependencies, and reassemble must call super first. Methods like deactivate and dispose must call super last.

Flutter’s State lifecycle methods have a specific contract about when super should be called. Calling super.initState() after your own initialization code can lead to errors because the framework expects to set up its own state first. Conversely, calling super.dispose() before your cleanup code means your resources are released while the framework has already torn down its own state. The quick fix automatically moves the super call to the correct position.

See also: State.initState | State.dispose

class _BadInitStateState extends State<BadInitState> {
String _data = '';
@override
void initState() {
_data = 'Hello'; // super.initState() should come before this
super.initState();
}
@override
Widget build(BuildContext context) => const SizedBox();
}
class _BadDisposeState extends State<BadDispose> {
@override
void dispose() {
super.dispose(); // super.dispose() should come after cleanup
debugPrint('cleanup');
}
@override
Widget build(BuildContext context) => const SizedBox();
}
class _BadDeactivateState extends State<BadDeactivate> {
@override
void deactivate() {
super.deactivate(); // super.deactivate() should come after cleanup
debugPrint('deactivating');
}
@override
Widget build(BuildContext context) => const SizedBox();
}
class _GoodInitStateState extends State<GoodInitState> {
String _data = '';
@override
void initState() {
super.initState(); // First
_data = 'Hello';
}
@override
Widget build(BuildContext context) => const SizedBox();
}
class _GoodDisposeState extends State<GoodDispose> {
@override
void dispose() {
debugPrint('cleanup');
super.dispose(); // Last
}
@override
Widget build(BuildContext context) => const SizedBox();
}
class _GoodDeactivateState extends State<GoodDeactivate> {
@override
void deactivate() {
debugPrint('deactivating');
super.deactivate(); // Last
}
@override
Widget build(BuildContext context) => const SizedBox();
}

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

To turn it off:

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