avoid-classes-with-only-static-members

Same as avoid_classes_with_only_static_members in Dart lints

Path
lint/complexity/avoid-classes-with-only-static-members
Default
Off, opt in
Origin
Dart lints
Rule
30 of 512

Flags a class that declares only static members.

Dart is not Java: free-standing functions and top-level variables do not need a class around them. A class whose every member is static — and which therefore is never instantiated for behavior of its own — only adds a namespace that a library (optionally imported with a prefix) already provides. Move the members to the top level instead.

class DateUtils {           // flagged
  static DateTime mostRecent(List<DateTime> dates) => dates.last;
}

DateTime mostRecent(List<DateTime> dates) => dates.last; // preferred

A class holding only static const fields (a group of constants) is allowed, as are sealed classes and classes with a non-default constructor. Like the analyzer, a class is skipped when its merged interface contains an instance member owned by a class other than Object; members replaced by mixin declarations do not count. Without resolution, only local mixins are considered member-free supertypes.

Invalid

example.dartdart
// Only static methods: move them to the top level.
class DateUtils {
  static DateTime mostRecent(List<DateTime> dates) =>
      dates.reduce((a, b) => a.isAfter(b) ? a : b);
}

// Static non-const fields count as instance-free state.
class _Favorites {
  static int mostFavorite = 0;
}

// A static getter induces a non-const field.
class Config {
  static String get name => 'config';
}

// A default constructor does not make the class a real type.
class Helpers {
  Helpers();
  static void run() {}
  static const int retries = 3;
}

// `extends Object` adds no members.
abstract class Registry extends Object {
  static final List<String> names = [];
  static void register(String name) => names.add(name);
}

// Static setter plus static method.
class Counter {
  static int _count = 0;
  static set count(int value) => _count = value;
  static void reset() => _count = 0;
}

// Unlike members inherited from a class, mixin members do not exempt this lint.
mixin Greets {
  void greet() {}
}
class Greeter with Greets {
  static void helper() {}
}

// Empty and static-only parents do not contribute instance members.
class EmptyParent {}

class ExtendsEmpty extends EmptyParent {
  static int value = 1;
}

class ImplementsEmpty implements EmptyParent {
  static int value = 1;
}

class StaticParent {
  static int value = 1;
}

class ExtendsStatic extends StaticParent {
  static void helper() {}
}

mixin class StaticMixinClass {
  static int value = 1;
}

// Only the merged interface's owners count, not overridden class declarations.
class MethodParent {
  void run() {}
}
mixin ReplacesMethod on MethodParent {
  void run() {}
}
class ReplacedMethod extends MethodParent with ReplacesMethod {
  static void helper() {}
}

class AccessorParent {
  int get value => 0;
  set value(int value) {}
}
mixin ReplacesAccessors {
  int get value => 1;
  set value(int value) {}
}
class ReplacedAccessors extends AccessorParent with ReplacesAccessors {
  static void helper() {}
}
Classes should define instance members.
1// Only static methods: move them to the top level.
2class DateUtils {
Classes should define instance members.
7// Static non-const fields count as instance-free state.
8class _Favorites {
Classes should define instance members.
12// A static getter induces a non-const field.
13class Config {
Classes should define instance members.
17// A default constructor does not make the class a real type.
18class Helpers {
Classes should define instance members.
24// `extends Object` adds no members.
25abstract class Registry extends Object {
Classes should define instance members.
30// Static setter plus static method.
31class Counter {
Classes should define instance members.
40}
41class Greeter with Greets {
Classes should define instance members.
47
48class ExtendsEmpty extends EmptyParent {
Classes should define instance members.
51
52class ImplementsEmpty implements EmptyParent {
Classes should define instance members.
55
56class StaticParent {
Classes should define instance members.
59
60class ExtendsStatic extends StaticParent {
Classes should define instance members.
63
64mixin class StaticMixinClass {
Classes should define instance members.
74}
75class ReplacedMethod extends MethodParent with ReplacesMethod {
Classes should define instance members.
86}
87class ReplacedAccessors extends AccessorParent with ReplacesAccessors {

Valid

example.dartdart
// A group of constants is allowed.
class Color {
  static const red = '#f00';
  static const green = '#0f0';
  static const blue = '#00f';
}

// Instance members make it a real class.
class Point {
  final int x;
  Point(this.x);
  static Point origin() => Point(0);
}

// Sealed classes are exempt.
sealed class Shape {
  static Shape? parse(String text) => null;
}

// A named constructor is not a default constructor.
class Logger {
  Logger.named();
  static void log(String message) {}
}

// A supertype other than Object may contribute instance members.
class Base {
  void run() {}
}

class Derived extends Base {
  static void helper() {}
}


class Impl implements Base {
  @override
  void run() {}
  static void helper() {}
}

// An empty class has no members at all.
class Marker {}

// Top-level functions instead of a static-only class.
DateTime mostRecent(List<DateTime> dates) => dates.first;

// A mixin class's instance members also provide a real interface.
mixin class InstanceMixinClass {
  void run() {}
}

class InheritsMixinClass extends InstanceMixinClass {
  static void helper() {}
}

// One surviving class-owned member still exempts the class.
class TwoMethods {
  void first() {}
  void second() {}
}
mixin ReplacesFirst {
  void first() {}
}
class PartiallyReplaced extends TwoMethods with ReplacesFirst {
  static void helper() {}
}

// Getter and setter ownership must remain separate.
class AccessorParent {
  int get value => 0;
  set value(int value) {}
}
mixin ReplacesGetter {
  int get value => 1;
}
class RetainsSetter extends AccessorParent with ReplacesGetter {
  static void helper() {}
}

Configure avoid-classes-with-only-static-members

Set its severity in falcon.json. See the configuration guide.

falcon.jsonjson
{
  "linter": {
    "rules": {
      "complexity": {
        "avoid-classes-with-only-static-members": "error"
      }
    }
  }
}

Or silence one occurrence with a suppression comment; the reason is required.

example.dartdart
// falcon-ignore lint/complexity/avoid-classes-with-only-static-members: <reason>