Sparround

Variables and types

Dart gives you three practical ways to declare a variable:

  • var — declaration with type inference. The type is inferred from the first assignment and does not change: var n = 1; then n = 'hello'; is a compile error.
  • final — assigned once, but the value may be computed at runtime: final now = DateTime.now();
  • const — the value must be fully known at compile time: const pi = 3.14;

You can also write the type explicitly: int count = 0;. Team convention is usually: be explicit in public APIs, and final + inference is enough for locals.

The difference between final and const is asked in almost every interview.

  • final freezes the reference: final list = [1, 2]; followed by list.add(3); is perfectly legal — the variable cannot point at a new list, but the list itself is mutable.
  • const is deep immutability: const list = [1, 2]; creates an immutable list, and list.add(3) throws UnsupportedError at runtime.

On top of that, const gives you canonicalisation: the same const expression produces a single object for the whole program, so identical(const [1, 2], const [1, 2]) is true.

Property`var``final``const`
ReassignableYesNoNo
When value is knownRuntimeRuntimeCompile time
Object contents mutable?YesYesNo — deeply immutable
CanonicalisationNoNoYes
Can be an instance field?YesYesOnly as `static const`

Numeric types. num is the abstract base; int and double are its subtypes. But int is not a subtype of `double` — this is the classic trap:

  • double x = 5; works, because the compiler reads the numeric literal as 5.0 (literals only!).
  • int n = 5; double x = n; is a compile error — you need n.toDouble().

Another difference: / always returns a double (7 / 2 == 3.5), and truncating division is ~/ (7 ~/ 2 == 3). On the web (dart2js) an int is really a JS number, so don't rely on full 64-bit integers there.

`dynamic` vs `var`. var means "infer the type for me" — the type is still statically checked. dynamic means "turn checking off": dynamic d = 5; d.foo(); compiles and throws NoSuchMethodError at runtime. Use dynamic only at untyped boundaries such as JSON, and convert to a real type immediately.

Interview tip. "Why does const matter in Flutter?" is a classic. A strong answer has two halves: (1) a const object is created once at compile time and canonicalised, so it is not re-allocated on every rebuild; (2) the framework can compare with identical() and skip work for that subtree because it provably did not change. A weak answer is "it's good for performance" and nothing more — explain why.

The most common mistake is presenting final and const as two names for the same thing. Say the difference in one sentence: "`final` is single assignment with a value that may be computed at runtime; `const` is a compile-time constant and deeply immutable."

📚 Sources and documentation