Skip to main content
Extensions provide a mechanism to add functionality to existing libraries without modifying the underlying source code or creating subclasses. Introduced in Dart 2.7, extensions allow developers to define new methods, getters, setters, and operators for a specific type. These members are resolved statically at compile-time.

Syntax Declaration

Extensions are declared using the extension keyword followed by an optional name and the on clause, which specifies the type being extended.
extension ExtensionName on TargetType {
  ReturnType methodName(Parameters params) {
    // 'this' refers to the instance of TargetType
    return ...;
  }
}

Static Resolution

Unlike class inheritance or mixins, extensions do not modify the runtime type of the object. Extension methods are statically dispatched. The compiler resolves the method call based on the static type of the variable, not the runtime type of the object. If a variable is typed as dynamic, extension methods cannot be invoked on it, as the compiler lacks the static type information required for resolution.

Member Capabilities

Extensions can define the following members:
  • Instance methods
  • Operators
  • Getters and Setters
  • Static fields and methods
Limitation: Extensions cannot declare instance fields. Since extensions do not alter the memory layout of the object, they cannot hold state.

Unnamed Extensions

If an extension is only used within the library where it is declared, the name can be omitted. Unnamed extensions are visible only within their declaring library.
extension on String {
  bool get isBlank => trim().isEmpty;
}

Precedence and Shadowing

Dart enforces a strict precedence rule when resolving member invocations:
  1. Class Members: Members defined on the target class (or inherited from superclasses) always take precedence.
  2. Extension Members: Extension members are only considered if no matching member exists on the class.
If an extension defines a member with the same name as a member of the class, the class member shadows the extension member. The compiler will invoke the class member without generating an error or warning.

Explicit Invocation and Conflict Resolution

Explicit invocation is required in two specific scenarios:
  1. Bypassing Shadowing: To invoke an extension member that is shadowed by a class member.
  2. Resolving Ambiguity: When two different extensions are in scope and define the same member name for the same type.
To explicitly invoke an extension member, the object is wrapped in the extension name, functioning syntactically like a constructor call or static wrapper.
extension A on String {
  void parse() { ... }
}

extension B on String {
  void parse() { ... }
}

void main() {
  String input = "data";
  
  // Explicitly invoking the method from extension A
  A(input).parse();
  
  // Explicitly invoking the method from extension B
  B(input).parse();
}

Generic Extensions

Extensions can be generic, allowing them to apply to types with specific type parameters.
extension ListExtensions<T> on List<T> {
  int get doubleLength => length * 2;
  
  List<T> operator - (int count) {
    // Implementation logic
    return this;
  }
}
Master Dart with Deep Grasping Methodology!Learn More