prefer-const-declarations

Same as prefer_const_declarations in Dart lints

Group
Style
Path
lint/style/prefer-const-declarations
Default
On (recommended)
Origin
Dart lints
Rule
329 of 512
recommended

Flags final declarations whose initializers are all constant expressions.

A final local variable, top-level variable, or static field initialized with a constant expression can be declared const instead. Plain collection literals and constructor calls without const are not constant expressions. Instance fields and for-loop initializer declarations are never reported, and every declarator must qualify. Diagnostic spans include local metadata and documentation, and include late but exclude static on fields. They end at the last declarator, before trailing trivia and the semicolon.

Constant eligibility uses the shared lexical constant checker and evaluator, including declaration-time local values, type parameters, deferred types, and failed arithmetic, casts, or non-primitive equality. Analyzer's lint treats generic type literals and constructor tear-offs as constants even with type parameters or deferred type arguments. Abstract generative constructor tear-offs have the same lint-only treatment. These parity quirks do not guarantee that the suggested const declaration compiles.

Invalid

example.dartdart
final o = const <int>[];

const base = 2;
final derived = base * 21;

final String greeting = 'hello', other = 'world';

final Map<String, int> lookup = const {'a': 1};

enum Color { red, green }

final favorite = Color.red;

class A {
  static final o = const <int>[];

  static const limit = 10;
  static final doubled = limit * 2;

  static final interpolated = 'n=${1 + 2}';
}

class Point {
  const Point(this.x, this.y);
  final int x;
  final int y;
}

final origin = const Point(0, 0);

void locals() {
  final a = 1;
  final flag = !true && 3 > 2 ? null : -1;
  const step = 5;
  final next = step + 1;
  final sym = #name;
  final viaClass = A.limit;
  print([a, flag, next, sym, viaClass]);
}

final tuple = const ("first", 2, true);
final implicitTuple = (number: 123, name: "Main");

void typeTests() {
  const c = 1;
  final typeTest = c is int;
  final cast = c as num;
  final interpolatesConstant = 'c=$c';
  final length = 'abc'.length;
  print([typeTest, cast, interpolatesConstant, length]);
}

void forBodies() {
  for (final i = 0; i < 1;) {
    final body = 1;
    print(body);
    break;
  }
  for (final callback = () {
    final nested = 1;
    return nested;
  }; callback() > 0;) {
    break;
  }
}

int topLevelFunction() => 0;

class Factory {
  const Factory.origin();
  static int create() => 0;
  static const prefix = 'p';
  static final prefixed = prefix + 'q';
}

final functionTearOff = topLevelFunction;
final staticTearOff = Factory.create;
final typeLiteral = Factory;
final colors = Color.values;
final Factory dotShorthand = const .origin();
final equality = 1 == 2.0;
final coalesced = null ?? 'fallback';
final numericMix = 1 + 2.5 * base - 7 ~/ 2;
final bits = (1 << 3) & 0xff | (true ^ false ? 1 : 0);
Use `const` for final variables initialized to a constant value.
1final o = const <int>[];
Use `const` for final variables initialized to a constant value.
3const base = 2;
4final derived = base * 21;
Use `const` for final variables initialized to a constant value.
5
6final String greeting = 'hello', other = 'world';
Use `const` for final variables initialized to a constant value.
7
8final Map<String, int> lookup = const {'a': 1};
Use `const` for final variables initialized to a constant value.
11
12final favorite = Color.red;
Use `const` for final variables initialized to a constant value.
14class A {
15 static final o = const <int>[];
Use `const` for final variables initialized to a constant value.
17 static const limit = 10;
18 static final doubled = limit * 2;
Use `const` for final variables initialized to a constant value.
19
20 static final interpolated = 'n=${1 + 2}';
Use `const` for final variables initialized to a constant value.
28
29final origin = const Point(0, 0);
Use `const` for final variables initialized to a constant value.
31void locals() {
32 final a = 1;
Use `const` for final variables initialized to a constant value.
32 final a = 1;
33 final flag = !true && 3 > 2 ? null : -1;
Use `const` for final variables initialized to a constant value.
34 const step = 5;
35 final next = step + 1;
Use `const` for final variables initialized to a constant value.
35 final next = step + 1;
36 final sym = #name;
Use `const` for final variables initialized to a constant value.
36 final sym = #name;
37 final viaClass = A.limit;
Use `const` for final variables initialized to a constant value.
40
41final tuple = const ("first", 2, true);
Use `const` for final variables initialized to a constant value.
41final tuple = const ("first", 2, true);
42final implicitTuple = (number: 123, name: "Main");
Use `const` for final variables initialized to a constant value.
45 const c = 1;
46 final typeTest = c is int;
Use `const` for final variables initialized to a constant value.
46 final typeTest = c is int;
47 final cast = c as num;
Use `const` for final variables initialized to a constant value.
47 final cast = c as num;
48 final interpolatesConstant = 'c=$c';
Use `const` for final variables initialized to a constant value.
48 final interpolatesConstant = 'c=$c';
49 final length = 'abc'.length;
Use `const` for final variables initialized to a constant value.
54 for (final i = 0; i < 1;) {
55 final body = 1;
Use `const` for final variables initialized to a constant value.
59 for (final callback = () {
60 final nested = 1;
Use `const` for final variables initialized to a constant value.
72 static const prefix = 'p';
73 static final prefixed = prefix + 'q';
Use `const` for final variables initialized to a constant value.
75
76final functionTearOff = topLevelFunction;
Use `const` for final variables initialized to a constant value.
76final functionTearOff = topLevelFunction;
77final staticTearOff = Factory.create;
Use `const` for final variables initialized to a constant value.
77final staticTearOff = Factory.create;
78final typeLiteral = Factory;
Use `const` for final variables initialized to a constant value.
78final typeLiteral = Factory;
79final colors = Color.values;
Use `const` for final variables initialized to a constant value.
79final colors = Color.values;
80final Factory dotShorthand = const .origin();
Use `const` for final variables initialized to a constant value.
80final Factory dotShorthand = const .origin();
81final equality = 1 == 2.0;
Use `const` for final variables initialized to a constant value.
81final equality = 1 == 2.0;
82final coalesced = null ?? 'fallback';
Use `const` for final variables initialized to a constant value.
82final coalesced = null ?? 'fallback';
83final numericMix = 1 + 2.5 * base - 7 ~/ 2;
Use `const` for final variables initialized to a constant value.
83final numericMix = 1 + 2.5 * base - 7 ~/ 2;
84final bits = (1 << 3) & 0xff | (true ^ false ? 1 : 0);

Valid

example.dartdart
const o = <int>[];

// Non-const collection literals create fresh mutable values.
final list = <int>[];
final map = {'a': 1};
final set = {1, 2};

// Only every declarator being constant qualifies.
final one = 1, now = DateTime.now();

// Instance fields are out of scope.
class A {
  static const o = <int>[];
  final instance = 1;
  static var mutable = 1;
  static final computed = DateTime.now();
}

var counter = 0;
final fromVariable = counter;

// Implicit `new` is not constant even for const constructors.
class Point {
  const Point(this.x, this.y);
  final int x;
  final int y;
}

final implicit = Point(0, 0);

// Unknown names cannot be proven constant.
final unknown = somewhereElse;

late final lazy = DateTime.now();

void locals(int parameter) {
  const c = 1;
  final fromParam = parameter;
  final fromCall = c.toString();
  final noInit;
  noInit = 2;
  final interpolatedVar = 'value $parameter';
  print([fromParam, fromCall, noInit, interpolatedVar]);
}

const shadowed = 1;

void shadowing(int shadowed) {
  // The parameter shadows the top-level constant.
  final copy = shadowed;
  print(copy);
}

void loops(List<int> values) {
  for (final value in values) {
    final copy = value;
    print(copy);
  }
}

// For-loop initializer declarations are not variable declaration statements.
void forInitializers() {
  for (final int i = 0; i < 1;) {
    print(i);
    break;
  }
  for (final first = 0, second = 1; first < second;) {
    break;
  }
}

void divide() {
  final bad = 1 ~/ 0;
  print(bad);
}

final nonConstantTuple = (1, () {});

// Operators are constant only for the operand types the language allows.
const separatorWidth = 80;
final repeated = '-' * separatorWidth;
final repeatedLiteral = 'ab' * 2;
const numbers = [1, 2];
final joined = const [0] + numbers;

class Holder {
  static const label = 'x';
  static final labelTimesTwo = label * 2;
  int instanceMethod() => 0;
}

final instanceTearOff = Holder().instanceMethod;

Configure prefer-const-declarations

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

falcon.jsonjson
{
  "linter": {
    "rules": {
      "style": {
        "prefer-const-declarations": "error"
      }
    }
  }
}

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

example.dartdart
// falcon-ignore lint/style/prefer-const-declarations: <reason>