avoid_unnecessary_overrides
v0.4.0 Warning Fix State Management
Warns when a class or mixin overrides a method, getter, or setter without adding any logic beyond calling super. This includes pass-through methods that forward all arguments unchanged, getters that only return super.getter, setters that only assign super.setter, and abstract redeclarations.
Why use this rule
Section titled “Why use this rule”Overrides that only delegate to super add visual noise without changing behavior. They make classes harder to scan and can mislead readers into thinking the override does something meaningful. Removing them keeps the codebase lean and makes intentional overrides stand out.
See also: Effective Dart: Usage | Dart lint: unnecessary_overrides
Overrides left behind after the body that justified them was removed. Each one reads like it does something:
class _CartPageState extends State<CartPage> { @override void initState() { super.initState(); }
@override void dispose() { super.dispose(); }
@override Widget build(BuildContext context) => const Text('Cart');}Getters and setters
Section titled “Getters and setters”The SDK’s unnecessary_overrides stops at methods. This rule also reports the
accessor forms, which is where they tend to accumulate:
class TimestampedRepository extends BaseRepository { @override String get name => super.name;
@override set name(String value) => super.name = value;}Abstract redeclarations
Section titled “Abstract redeclarations”Restating an inherited abstract member adds nothing — the subclass already has to implement it:
abstract class BaseRepository { Future<void> refresh();}
abstract class CachedRepository extends BaseRepository { @override Future<void> refresh(); // adds nothing}Delete the pass-throughs. Keep an override only when it changes something:
class _CartPageState extends State<CartPage> { @override Widget build(BuildContext context) => const Text('Cart');}class TimestampedRepository extends BaseRepository { // Adds behaviour — kept. @override String get name => super.name.toUpperCase();
@override Future<void> refresh() async { _lastRefresh = DateTime.now(); await super.refresh(); }
DateTime? _lastRefresh;}An override with an intentionally empty body is not a pass-through — it suppresses the inherited behaviour, and is left alone:
class SilentRepository extends BaseRepository { // Deliberately does nothing — no super call. @override Future<void> refresh() async {}}Known limitations
Section titled “Known limitations”The same exemptions the SDK rule applies hold here: an override is not
reported when it carries a documentation comment, an annotation other than
@override (@protected, @Deprecated), or a covariant parameter — and
noSuchMethod is never reported. Those are legitimate reasons to override a
member without changing its body.
A forwarding override only counts as a pass-through when the arguments go
through unchanged. super.bar(x + 1, y) is a real override.
Configuration
Section titled “Configuration”This rule is in the opinionated preset, so it is on with
preset: opinionated, or by name:
rules: avoid_unnecessary_overrides: trueTo turn it off again:
rules: avoid_unnecessary_overrides: falseTo keep the rule on but skip certain paths, use per-rule exclude.
Related rules
Section titled “Related rules”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.avoid_mounted_in_setstate— Detect mounted checks inside setState callbacks.