Skip to main content
A final field in Dart is a single-assignment variable whose value is determined at runtime and cannot be modified once initialized. Unlike const, which requires a compile-time constant, a final variable can be assigned a value based on logic executed when the program runs, but that assignment can occur only once.

Syntax

A final field is declared using the final keyword. Type annotation is optional if the type can be inferred.
// With explicit type annotation
final String protocol = 'HTTPS';

// With type inference
final port = 8080; // Inferred as int

// Assigned via runtime function
final timestamp = DateTime.now(); 

Initialization Rules

In the context of a class, a final instance variable must be initialized before the constructor body executes. There are three mechanisms to achieve this:
  1. Inline Initialization: Assigning the value directly at the point of declaration.
  2. Initializing Formal: Using the this.variableName syntax in the constructor parameters.
  3. Initializer List: Assigning the value in the constructor’s initializer list (after the : and before the body).
class Connection {
  // 1. Inline Initialization
  final String defaultHeader = 'Keep-Alive';
  
  final String url;
  final int timeout;

  // 2. Initializing Formal (url)
  // 3. Initializer List (timeout)
  Connection(this.url, int configuredTimeout) 
      : timeout = configuredTimeout * 1000; 
}

Reference Immutability vs. Object Mutability

The final keyword enforces referential immutability, not deep immutability.
  • Reference: You cannot reassign the variable to point to a different object in memory.
  • Object State: If the object referenced by the final variable is mutable (such as a List or a class with non-final fields), its internal state can still be modified.
final List<int> numbers = [1, 2, 3];

// Allowed: Modifying the internal state of the object
numbers.add(4); 

// Error: Reassigning the reference
// numbers = [1, 2, 3, 4]; // Compilation Error

Late Final Initialization

The late modifier can be combined with final to defer initialization. A late final variable:
  1. Does not require immediate initialization in the constructor or at declaration.
  2. Must be assigned a value exactly once before it is accessed.
  3. Throws a runtime exception if assigned a value more than once.
class LazyLoader {
  late final int computedValue;

  void calculate() {
    computedValue = 42; // Allowed once
  }
  
  void recalculate() {
    // computedValue = 100; // Throws LateInitializationError at runtime
  }
}

Static Final Fields

When a field is declared as static final, it becomes a class-level variable. It is lazily initialized, meaning the initializer expression is not evaluated until the field is accessed for the first time.
class Config {
  // 'heavyComputation()' is only called when Config.apiKey is accessed
  static final String apiKey = heavyComputation();
}
Master Dart with Deep Grasping Methodology!Learn More