skip to content

In Dart, what is an extension method, how do you declare one with `extension ... on`, and what may it contain?

level: juniorimportance: must knowfreq 60%

answer

  1. add members without editing the type
  2. this is the receiver
  3. methods, getters, setters, operators
  4. no fields, no constructors
  5. import the library to use it

basics

~20 s

An extension method adds a member to an existing type, including types you do not own, via extension Name on Type { ... }. It may declare methods, getters, setters, operators and static members, but no instance fields, constructors or abstract members.

solid answer

~40 s

An extension adds members to a type without changing or subclassing it: `extension NumberParsing on String { int parseInt() => int.parse(this); }` makes `'42'.parseInt()` compile wherever the declaring library is imported. Inside the body, `this` is the receiver. Extensions may declare methods, getters, setters, operators, and static fields and methods. They may not declare instance fields, constructors, abstract members, or members named like one of `Object`'s (`toString`, `==`, `hashCode`). An extension can be generic (`extension MyFancyList<T> on List<T>`) and can target a nullable type such as `String?`, in which case it can be called on `null`. The name is optional, but an unnamed extension is visible only in its own library.

code

dart · 19 lines
dart
extension NumberParsing on String {
  int parseInt() => int.parse(this);
}

extension BlankCheck on String? {
  bool get isNullOrBlank => this == null || this!.trim().isEmpty;
}

extension on List<int> {
  // unnamed: visible only in this library
  int get total => fold(0, (a, b) => a + b);
}

void main() {
  print('42'.parseInt());   // 42
  String? name;
  print(name.isNullOrBlank); // true
  print([1, 2, 3].total);    // 6
}

go deeper

for a junior

Recall the extension Name on Type syntax, that this is the receiver, and that you must import the declaring library to use it.

for a middle

Explain the allowed members, why instance fields and constructors are impossible, and how generic and nullable on types behave.

for a senior

Keep exported extensions small, named and stateless, and avoid scattering overlapping helpers that invite conflicts across packages.

for a principal

Set conventions on where extensions live and which types a shared package may extend, so helpers stay discoverable without polluting common types.

## Why extension methods exist You often want a convenience on a type you cannot change: `String` from `dart:core`, a model class from a generated package, `BuildContext` from Flutter. Subclassing is impossible or pointless (you would have to create your own subclass instances), and a top-level helper function (`parseInt(s)`) reads worse than `s.parseInt()`. Dart's **extension methods** solve this by letting a library add members to an existing type, visible to any code that imports that library. ## Declaring an extension The syntax, per dart.dev: ```dart extension NumberParsing on String { int parseInt() => int.parse(this); double parseDouble() => double.parse(this); } ``` - `extension` introduces the declaration; **`NumberParsing`** is its optional name. - **`on String`** is the **on type**: the static type the members attach to. - Inside the body, **`this`** (often implicit) is the receiver — the string the method was called on. Usage is just a normal member call once the library is imported: ```dart import 'string_apis.dart'; void main() { print('42'.parseInt()); // 42 } ``` IDEs suggest extension members in code completion alongside the type's own members. ## What an extension may contain | Allowed | Not allowed (analyzer message) | |---|---| | instance methods | instance fields ("Extensions can't declare instance fields") | | getters and setters | constructors ("Extensions can't declare constructors") | | operators, e.g. `operator -()` | abstract members ("Extensions can't declare abstract members") | | static fields and static methods | members named like `Object`'s, e.g. `toString`, `hashCode` | | type parameters on the extension | a `super` call (an extension has no superclass) | The no-fields rule is the big one: an extension **cannot store per-object state**. If you need state, you need a class, a mixin, or an `Expando`. ## Generic and nullable on types Extensions can be **generic**; the type parameter is bound from the static type of the receiver: ```dart extension MyFancyList<T> on List<T> { int get doubleLength => length * 2; List<List<T>> split(int at) => [sublist(0, at), sublist(at)]; } ``` They can also target a **nullable** type, which makes them callable on `null` without `?.`: ```dart extension BlankCheck on String? { bool get isNullOrBlank => this == null || this!.trim().isEmpty; } String? name; print(name.isNullOrBlank); // true ``` ## Named versus unnamed The name is optional: ```dart extension on String { bool get isBlank => trim().isEmpty; } ``` An **unnamed extension** is visible **only in the library that declares it**, and, having no name, cannot be applied explicitly to settle a conflict. Use unnamed extensions for private helpers inside one file, and name every extension you export. ## Where interviewers go next 1. Why the call fails on a `dynamic` receiver (static resolution). 2. What happens when the type already has a member of the same name (the real member wins). 3. How to resolve two extensions defining the same member. 4. How extension methods differ from **extension types**, which create a new static type instead of adding members to every instance. ## Typical Flutter uses Extensions are common in Flutter code for small, stateless conveniences: formatting a `DateTime`, turning a `Duration` into a label, or shortening a theme lookup. They must stay stateless and side-effect-light; anything that needs to remember a value between calls belongs elsewhere.

  • In Dart, why can't an extension declare an instance field?
    An extension does not create objects or change their layout; it only adds statically resolved members to an existing type. There is nowhere to store a per-receiver value, so the analyzer rejects instance fields. Static fields are allowed because they belong to the extension declaration, not to each receiver.
  • In Dart, can an extension on String? be called on a null value?
    Yes. When the on type is nullable, `this` inside the extension can be `null`, and a call such as `name.isNullOrBlank` compiles on a `String?` without `?.`. The extension body must handle the null case itself.

saying these in an interview costs you the question

  • Extensions can add instance fields to an existing class.
  • An extension creates a subclass of the on type.
  • Extensions can override toString or == for a type.
  • An unnamed extension is visible to every importing library.
  • Extension methods only work on classes you wrote yourself.