Skip to content
AgileLogics
5 min read

Extension Methods

Prerequisites: Classes and objects

An extension adds methods, getters, and operators to an existing type — even one you didn't write, like String or int — without changing or subclassing it.

Example

extension StringHelpers on String {
  String get initials => split(' ')
      .where((w) => w.isNotEmpty)
      .map((w) => w[0].toUpperCase())
      .join();

  String capitalize() => isEmpty ? this : this[0].toUpperCase() + substring(1);
}

extension on int {
  Duration get minutes => Duration(minutes: this);
}

void main() {
  print('grace brewster hopper'.initials);
  print('dart'.capitalize());
  print(5.minutes);
}

Expected output

GBH
Dart
0:05:00.000000
  • extension StringHelpers on String adds members to String. Inside, this is the string the method was called on, and its members can be used directly (split means this.split).
  • An extension without a name (extension on int) is only usable in its own file.
  • Extensions must be imported to be used in other files, like anything else.

Printing a Duration shows hours, minutes, seconds, and microseconds.

Extensions on nullable types

extension on String? {
  String orPlaceholder() => (this == null || this!.trim().isEmpty) ? '—' : this!;
}

void main() {
  String? missing;
  print(missing.orPlaceholder());
  print('Ada'.orPlaceholder());
}
—
Ada

What extensions can't do

  • They can't add fields (instance variables).
  • They're resolved from the variable's static type at compile time, so they don't work on dynamic values and can't override a class's own methods.

Common mistakes

  • Adding very general names to common types. Keep extension names specific, so they don't clash with other packages or future Dart versions.