Variables & Type System

40 minâ€ĸtext

Theory & Concepts

Understanding Dart's Variable System

Dart is a strongly typed language with optional type annotations and powerful type inference. This gives you the best of both worlds: safety and conciseness.

💡 Why This Matters: Proper variable usage prevents bugs, makes code self-documenting, and enables better IDE support with autocomplete and error checking.

Variable Declaration Options

Dart offers four main ways to declare variables:

1. var - Type Inferred

  • Dart automatically determines the type from the initial value
  • Type cannot change after assignment
  • Use when type is obvious from context

2. final - Runtime Constant

  • Value set once at runtime and cannot change
  • Type can be inferred or explicit
  • Similar to JavaScript's const

3. const - Compile-Time Constant

  • Value must be known at compile time
  • Deeply immutable (all nested values are const too)
  • More restrictive than final

4. Explicit Type - Clear Declaration

  • Specify the exact type
  • Use when type isn't obvious or for API clarity
  • Required for uninitialized variables

Type System Fundamentals

Built-in Types:

Numbers:

  • int - Integers (whole numbers): 42, -100
  • double - Floating-point: 3.14, -0.5
  • num - Supertype of both int and double

Text:

  • String - Text data: 'Hello', "World"
  • Supports interpolation: 'Value: $variable'
  • Multi-line strings with triple quotes

Boolean:

  • bool - True or false values
  • Only true and false (no truthy/falsy)

Collections:

  • List - Ordered collection: [1, 2, 3]
  • Set - Unique values: {1, 2, 3}
  • Map - Key-value pairs: {'key': 'value'}

Dynamic vs Object

dynamic:

  • Disables type checking
  • Use sparingly (loses type safety)
  • Type errors found at runtime

Object:

  • Root of Dart's type hierarchy
  • More type-safe than dynamic
  • Requires explicit casting

âš ī¸ Best Practice: Avoid dynamic when possible. Use specific types or generics instead.

Type Inference Rules

Dart's type inference is smart:

  1. From initialization: var x = 5; → int
  2. From function return: var result = someFunction();
  3. Generic type inference: var list = [1, 2]; → List<int>

When to Use Each Declaration

Use var:

  • Type is obvious: var name = 'Alice';
  • Local variables with clear initialization

Use final:

  • Value won't change after initialization
  • Can't be known at compile time: final timestamp = DateTime.now();
  • Function parameters that shouldn't change

Use const:

  • Compile-time constants: const pi = 3.14159;
  • Deeply immutable data structures
  • Performance optimization (single instance)

Use explicit types:

  • Public APIs and class fields
  • Uninitialized variables: int? count;
  • When type isn't obvious

Common Pitfalls

Mistake 1: Trying to Reassign final/const

dart
final x = 10;
x = 20; // ❌ Error: Can't assign to final variable

Mistake 2: Using const for Runtime Values

dart
const now = DateTime.now(); // ❌ Error: Not a compile-time constant
final now = DateTime.now(); // ✅ Correct

Mistake 3: Overusing dynamic

dart
dynamic data = getData(); // ❌ Loses type safety
var data = getData(); // ✅ Better: maintains type

Summary

Key Takeaways:

  1. var = type inferred, can't reassign type
  2. final = set once at runtime, immutable
  3. const = compile-time constant, deeply immutable
  4. Use explicit types for clarity and uninitialized variables
  5. Avoid dynamic when possible

Best Practices:

  • Prefer final over var when value won't change
  • Use const for compile-time constants
  • Be explicit with public API types
  • Let type inference work for obvious cases

Remember: Strong typing catches bugs early and makes code self-documenting!

Lesson Content

Master Dart's variable declarations, type system, and type inference. Learn the differences between var, final, const, and explicit types.

Code Example258 lines

Section 1 of 20 â€ĸ Lesson 1 of 8