Skip to main content
The null-aware index operator (?[]) conditionally accesses an element within a subscriptable object (such as a List or Map) only if that object is not null. If the receiver is null, the expression evaluates to null immediately; otherwise, it performs the standard index operation.

Syntax

receiver?[key]

Operational Semantics

When the expression e1?[e2] is executed:
  1. Receiver Evaluation: The expression e1 (the receiver) is evaluated.
  2. Null Check:
    • If e1 evaluates to null, the operation short-circuits. The index expression e2 is not evaluated, and the entire expression returns null.
    • If e1 is not null, e2 is evaluated, and the standard [] operator is invoked on e1 with the result of e2.

Logical Equivalence

The operator is syntactic sugar that ensures the receiver is evaluated exactly once. It is logically equivalent to assigning the receiver to a temporary variable before checking for null:
// Logical representation of: result = receiver?[index];
var tmp = receiver;
var result = (tmp == null) ? null : tmp[index];

Type Implications

The return type of a null-aware index operation is always nullable. The operator “lifts” the element type of the collection into a nullable type. Given a receiver of type List<T>?:
  • Direct Access: list[0] results in a compile-time error because the receiver might be null.
  • Null-Aware Access: list?[0] is valid and returns type T?.
Even if the underlying collection holds non-nullable types (T), the result of the expression must be T? to account for the case where the receiver itself is null.

Implementation Example

void main() {
  List<int>? nullableList = null;
  List<int> definedList = [10, 20, 30];

  // Scenario 1: Receiver is null
  // The expression short-circuits and returns null.
  int? result1 = nullableList?[0]; 
  assert(result1 == null);

  // Scenario 2: Receiver is not null
  // The standard index operator is called.
  int? result2 = definedList?[1];
  assert(result2 == 20);
  
  // Scenario 3: Short-circuiting side effects
  // Because nullableList is null, getIndex() is never called.
  int getIndex() {
    print("Calculating index...");
    return 0;
  }
  var result3 = nullableList?[getIndex()]; 
}
Master Dart with Deep Grasping Methodology!Learn More