DMarlon Studio

Lesson 7 · 7 of 20

Extension methods · Dart

Add methods and getters to int, String or List in Dart with extensions, generic ones included, without subclassing or changing those classes.

Dart · Extension methods in DMarlon Studio: the example's code and its output in the terminal

Extensions add methods and getters to types you do not own, such as int, String or List, without subclassing them or wrapping them in another class. You declare them with extension Name on Type and, inside, this is the value the member is called on.

MoneyFormat gives integers an eur getter, so 1999.eur returns 19.99 EUR. TextTools adds two helpers to String: capitalized (first letter upper case, the rest lower case) and toSlug(), which uses two regular expressions to turn a title into URL-friendly text. Notice that inside the extension you call trim() or substring() without a prefix, just as in a method of the class itself.

Stats extends Iterable<int>, so it works on lists, sets and any iterable of integers: total adds up with fold and average divides by length. Extensions combine naturally: basket.average.round().eur rounds the average and formats it with the first extension.

SafeIndex<T> is a generic extension: it works on a List of any type, and at(7) returns null instead of throwing a RangeError. Extensions are picked at compile time from the static type of the expression, so they do not work on a dynamic variable.

Code · main.dart

// Extensions add methods to types you do not own, like int or String.extension MoneyFormat on int {  String get eur => '${(this / 100).toStringAsFixed(2)} EUR';}extension TextTools on String {  String get capitalized =>      isEmpty ? this : this[0].toUpperCase() + substring(1).toLowerCase();  String toSlug() => trim()      .toLowerCase()      .replaceAll(RegExp(r'[^a-z0-9]+'), '-')      .replaceAll(RegExp(r'^-|-$'), '');}extension Stats on Iterable<int> {  int get total => fold(0, (sum, n) => sum + n);  double get average => isEmpty ? 0 : total / length;}// Generic extension: works on a List of any type.extension SafeIndex<T> on List<T> {  T? at(int index) => index >= 0 && index < length ? this[index] : null;}void main() {  print(1999.eur);  print('mADRID'.capitalized);  print('  Advanced Dart: Records & Patterns! '.toSlug());  final basket = [1250, 899, 4500];  print('Total ${basket.total.eur}, average ${basket.average.round().eur}');  print('Item 1: ${basket.at(1)}, item 7: ${basket.at(7)}');}

Output · dart run main.dart

19.99 EUR
Madrid
advanced-dart-records-patterns
Total 66.49 EUR, average 22.16 EUR
Item 1: 899, item 7: null

Key points

  • extension Name on Type adds members to types you do not own.
  • Inside the extension, this is the value it was called on.
  • They can be getters, methods and even operators.
  • They can be generic: extension X<T> on List<T>.
  • They are chosen by static type, so dynamic values miss them.

Exercise

Write an extension on String with an initials getter that returns the upper-case initials, so that 'ana belen ruiz'.initials gives ABR.