Skip to content

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.

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');
}

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;
}

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 {}
}

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.

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

many_lints.yaml
rules:
avoid_unnecessary_overrides: true

To turn it off again:

many_lints.yaml
rules:
avoid_unnecessary_overrides: false

To keep the rule on but skip certain paths, use per-rule exclude.