Classes, Enums, Extensions
copyWith
Immutable Update
An immutable class never changes its fields, so updates produce a new instance. copyWith takes nullable named parameters and uses ?? to fall back to this's value when the caller omits an argument. The original is unchanged.
Program
Play the program to build a Todo, copy it with done: true, and compare the two labels.
copy_with.dart
Replay: real traced execution (multi-file project)
class Todo {
final String title;
final bool done;
const Todo(this.title, {this.done = false});
Todo copyWith({String? title, bool? done}) => Todo(title ?? this.title, done: done ?? this.done);
String label() => '[${done ? 'x' : ' '}] $title';
}
void main() {
final original = Todo('Write notes');
final completed = original.copyWith(done: true);
print('${original.label()} -> ${completed.label()}');
}
original ← Todo(title: Write notes, done: false)
9void main() {10 final original = Todo('Write notes');11 final completed = original.copyWith(done: true);values this stepTodo(title: Write notes, done: false)originalresolved ← title fallback, done true
4const Todo(this.title, {this.done = false});5Todo copyWith({String? title, bool? done}) => Todo(title ?? this.title, done: done ?? this.done);6String label() => '[${done ? 'x' : ' '}] $title';values this steptitle fallback, done trueresolvedresolved ← Todo(title: Write notes, done: true)
4const Todo(this.title, {this.done = false});5Todo copyWith({String? title, bool? done}) => Todo(title ?? this.title, done: done ?? this.done);6String label() => '[${done ? 'x' : ' '}] $title';values this stepTodo(title: Write notes, done: true)resolvedcompleted ← Todo(title: Write notes, done: true)
10final original = Todo('Write notes');11final completed = original.copyWith(done: true);12print('${original.label()} -> ${completed.label()}');values this stepTodo(title: Write notes, done: true)completedreturn ← [ ] Write notes
5 Todo copyWith({String? title, bool? done}) => Todo(title ?? this.title, done: done ?? this.done);6 String label() => '[${done ? 'x' : ' '}] $title';7}values this step[ ] Write notesreturnfalsedoneWrite notestitlereturn ← [x] Write notes
5 Todo copyWith({String? title, bool? done}) => Todo(title ?? this.title, done: done ?? this.done);6 String label() => '[${done ? 'x' : ' '}] $title';7}values this step[x] Write notesreturntruedoneWrite notestitleprint('${original.label()} -> ${completed.label()}');
11 final completed = original.copyWith(done: true);12 print('${original.label()} -> ${completed.label()}');13}output[ ] Write notes -> [x] Write notesvalues this step[ ] Write notesoriginal.label()[x] Write notescompleted.label()
copyWith
`copyWith({String? title, bool? done})` builds a fresh `Todo`, picking new field values where the caller supplied them.
null-coalescing
`title ?? this.title` keeps the original value when the caller omits the argument; `done ?? this.done` does the same for `done`.
immutable update
`original` still has `done: false` after the call; `completed` is a separate instance with `done: true`.