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;thenn = '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.
finalfreezes the reference:final list = [1, 2];followed bylist.add(3);is perfectly legal — the variable cannot point at a new list, but the list itself is mutable.constis deep immutability:const list = [1, 2];creates an immutable list, andlist.add(3)throwsUnsupportedErrorat 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` |
|---|---|---|---|
| Reassignable | Yes | No | No |
| When value is known | Runtime | Runtime | Compile time |
| Object contents mutable? | Yes | Yes | No — deeply immutable |
| Canonicalisation | No | No | Yes |
| Can be an instance field? | Yes | Yes | Only 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 as5.0(literals only!).int n = 5; double x = n;is a compile error — you needn.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
- Variablesofficialdart.dev
- Built-in typesofficialdart.dev