What is cascade notation (..)?

Low PriorityAsked in ~35% of Flutter interviews

2 min read

Dart Syntax

OperatorReturnsUse when
.result of the callNormal method invocation
..the receiver (the object itself)Configure / mutate same object multiple times
?..receiver or nullReceiver might be null; first segment must be ?.., rest can stay ..

The .. is syntactic. It's not method chaining via return-this — it works on any method/field, including ones that return void.


Code in action

// Without cascade — repetitive
final btn = Button();
btn.text = 'Click me';
btn.color = Colors.blue;
btn.onTap = handleTap;
btn.build();

// With cascade — same result, less noise
final btn = Button()
  ..text = 'Click me'
  ..color = Colors.blue
  ..onTap = handleTap
  ..build();
// Why it matters: works with void-returning methods
final list = [1, 2]..add(3);                 // [1, 2, 3]
[1, 2].add(3);                               // returns void — list is unreachable

final buf = StringBuffer()
  ..write('Hello, ')
  ..write('world')
  ..writeln('!');
print(buf.toString());                       // "Hello, world!\n"

// Painters love it
final paint = Paint()
  ..color = Colors.indigo
  ..strokeWidth = 2
  ..style = PaintingStyle.stroke;

Null-aware cascade ?..

User? user = maybeUser();
user
  ?..name = 'Alice'                          // first segment is null-aware
  ..age  = 30;                               // subsequent ones stay `..` — already non-null

// Equivalent to:
if (user != null) {
  user.name = 'Alice';
  user.age  = 30;
}

The trick: only the first cascade needs ?... Once the chain enters, Dart already knows the receiver is non-null.


When to use cascades

SituationUse
Configure a builder-style object (Paint, Path, Form)✅ Cascade
Initialise a collection with several mutations✅ Cascade
Set a handful of properties on a freshly-constructed object✅ Cascade
You actually want the result of the call, not the receiver❌ Use .
Chain of different objects (a.b.c.d)❌ Not cascades — that's normal chaining

Common mistakes to avoid

// ❌ Expecting cascade to return the call result
final length = [1, 2, 3]..length;             // returns the list, NOT 3
// ✅ Use a normal access
final length = [1, 2, 3].length;

// ❌ Stray semicolon kills the chain
final paint = Paint()
  ..color = Colors.red;
  ..strokeWidth = 2;                          // ❌ standalone statement now

// ❌ Trying to call a getter through cascade and reuse it
final user = User()..name;                    // result is the User, not the name

// ❌ Mixing `..` and `.` on the same step
final x = Paint()..color.red;                 // ❌ accesses Color.red on color — usually a mistake

// ❌ Forgetting `?..` for nullable receivers
User? u = maybe();
u..name = 'Alice';                            // 💥 if u is null
// ✅ u?..name = 'Alice';

Interview follow-ups

  1. What does the cascade operator return? The receiver, regardless of what the called method returns. That's why [1, 2]..add(3) evaluates to the list — even though add returns void.

  2. How is cascade different from method chaining via return this? Method chaining only works when every method explicitly returns this, and you're tied to its return type. Cascade is syntactic: it works on any method or property — even void ones, even ones returning unrelated values. No API design tax required.

  3. When would you use ?.. vs ..? ?.. when the receiver might be null. Only the first cascade needs the ? — once you're past it, Dart treats subsequent calls as non-null.

  4. Can you nest cascades? Yes, with parentheses: obj..a.list..add(1) is a stylistic trap. Use .. to address the same receiver and parentheses to scope nested cascades on inner objects — but at that point, splitting into two statements is usually clearer.

How helpful was this content?

Please sign in to rate this article.