← Back to list

15.Extension Methods

A feature that allows you to add functionality to existing libraries and classes — even those you don’t own — without modifying the…

Aly Route · 2026-04-09 16:58 · 0 claps · 4.4 min read
#extension #dart
Open on Medium ↗

15.Extension Methods

A feature that allows you to add functionality to existing libraries and classes — even those you don’t own — without modifying the original source code.

· Implementation Syntax · Key Advantages of Extension Methods · Supported MembersSetter — GetterOperatorsStatic Members · Extension Name Optional · Test Your Knowledge

Implementation Syntax

extension <extension name>? on <type> { // <extension-name> is optional
(<member definition>)* // Can provide one or more <member definition>
}

// Basic Example: Adding parseInt() to the String class

extension NumberParsing on String {

  int parseInt() {
  return int.parse(this);
  }

  double parseDouble() {
  return double.parse(this);
  }
}

Key Advantages of Extension Methods

Adding functions to existing classes is better than creating old-fashioned “Helper Classes — extending class”.

1. Readability: It makes your code read like a natural English sentence.

  • The Old Way (Helper Function): You call a function and pass the object to it.
checkHabitable(planet) (Translation: Check habitability for… planet).
  • The New Way (Extension): You ask the object to check itself.
planet.isHabitable (Translation: Planet… is it habitable?).

2. Discoverability: When you use extensions, the new methods show up automatically in your IDE’s autocomplete list after type a dot (.) so you don’t need to memorize the name of some external StringUtils class.

3. Efficiency: You might think this convenience makes the app slow, but it is actually very fast because the compiler knows exactly which function to call while you are writing the code. It doesn’t have to “guess” or search for it while the app is running.

Supported Members

Extension methods aren’t limited to just methods. You can extend classes with:-

Setter — Getter

class User {
  String firstName = '';
  String lastName = '';
}

extension UserHelper on User {
  // 1. GETTER
  // Allows reading the full name like a variable: user.fullName
  String get fullName => '$firstName $lastName';

  // 2. SETTER
  // Allows writing the full name: user.fullName = "John Doe"
  // It splits the string and updates the original fields.
  set fullName(String name) {
    var parts = name.split(' ');

    if (parts.length >= 2) {
      this.firstName = parts[0];
      this.lastName = parts[1];
    } else {
      this.firstName = name;
      this.lastName = '';
    }
  }
}

void main() {
  User myUser = User();

  // Using the SETTER
  // We assign a string, and the extension logic handles the rest.
  myUser.fullName = "Ahmed Ali";

  // Check the original fields
  print(myUser.firstName); // Output: Ahmed
  print(myUser.lastName);  // Output: Ali

  // Using the GETTER
  print(myUser.fullName);  // Output: Ahmed Ali
}

Operators

extension StringMultiplier on String {
  // Using the (*) operator to repeat text
  String operator *(int times) {
    String result = '';
    for (int i = 0; i < times; i++) {
      result += this;
    }
    return result;
  }
}

void main() {
  String star = "*";

  // Instead of a loop, just multiply the string!
  print(star * 10); // Output: **********
  print("Hello " * 3); // Output: Hello Hello Hello 
}

Static Members

Normally, extension methods work on a specific variable (an instance) Example: myText.isValidEmail (You are asking myText to check itself).

Static Members are different. They belong to the Extension itself, not the variable. They are used to store constants or helper functions related to that class, but they don’t need a specific value to run.

  • Why use Static Members?

It helps with Organization. Instead of creating a separate class called DateUtils or Constants, you keep everything related to DateTime inside one neat Extension called DateHelper.

· Instance methods: “Hey variable, do this.”

· Static methods: “Hey Extension, give me this general tool.”

extension DateHelper on DateTime {
  // 1. Normal Method (Instance)
  // Needs a specific date to work.
  bool get isWeekend {
    return this.weekday == 6 || this.weekday == 7;
  }

  // 2. Static Member (Constant)
  // A fixed rule for everyone.
  static const int daysInWeek = 7;

  // 3. Static Method (Helper)
  // A general utility function.
  static DateTime getTomorrow() {
    return DateTime.now().add(Duration(days: 1));
  }
}

void main() {
  DateTime today = DateTime.now();

  // A. Using the Normal Method (From the variable)
  print(today.isWeekend); // ✅ Works
  // print(today.daysInWeek); // ❌ ERROR: static members are not in the variable!

  // B. Using the Static Member (From the Extension Name)
  // You must call 'DateHelper', not 'today'.
  print(DateHelper.daysInWeek); // ✅ Output: 7
  print(DateHelper.getTomorrow()); // ✅ Output: 2025-12-xx...
}

Extension Name Optional

You can name an extension (extension MyExt on String) or leave it nameless (extension on String)

1. Unnamed Extensions (The “Private” Mode)

If you don’t give it a name, the extension becomes Private, it means you can uuse it only inside the current file. So It keeps your global code clean. Other files won’t see it, so you don’t accidentally pollute the whole project with specific helpers.

2. Named Extensions (The “Public” Mode)

If you give it a name, the extension becomes Public (can be used anywhere) and helps solve Conflicts.

The Conflict Problem: Imagine you use two different extensions and both add a method called .show() to your Widget.

· Extension A: extension Info on String { void show() … }

· Extension B: extension Alert on String{ void show() … }

If you type myString.show(), dart gets confused: “Which one do you mean?”

1- Using the Name: You use the extension name as a wrapper to tell dart exactly which one to pick.

// Let's assume these extensions are defined in your project:

extension ReportFormatter on String {
  String format() => this.toUpperCase();
}

extension CardFormatter on String {
  String format() {
    if (this.isEmpty) return "";
    return this[0].toUpperCase() + this.substring(1).toLowerCase();
  }
}

void main() {
  String employeeName = "mohamed";

  // If you write: employeeName.format(); 
  // It will cause an ERROR because Dart doesn't know which one you want!

  // --- SECTION 1: EXPLICIT CALLS ---

  // 1. Use the version from ReportFormatter (Result: MOHAMED)
  String reportVersion = ReportFormatter(employeeName).format();
  print("Report Name: $reportVersion");

  // 2. Use the version from CardFormatter (Result: Mohamed)
  String cardVersion = CardFormatter(employeeName).format();
  print("ID Card Name: $cardVersion");
}

2- changing how you import the conflicting extension, using show or hide to limit the exposed API.

import 'report_utils.dart'; // Has format() for ALL CAPS
import 'card_utils.dart' hide CardFormatter; // Import file but ignore CardFormatter

void main() {
  String employeeName = "mohamed";

  // Now there is no conflict! 
  // Dart only sees the 'format' from report_utils.
  print(employeeName.format()); // Output: MOHAMED
}

3- import using a prefix

import 'report_utils.dart'; // Normal import
import 'card_utils.dart' as card; // Nicknamed 'card'

void main() {
  String employeeName = "mohamed";

  // 1. This uses the normal import (ReportFormatter)
  // We call it explicitly to be safe during conflicts
  print(ReportFormatter(employeeName).format()); // Output: MOHAMED

  // 2. This uses the one from the 'card' prefix
  print(card.CardFormatter(employeeName).format()); // Output: Mohamed
}

Test Your Knowledge


메타데이터
post_id
e7dd64b2df45
slug
15-extension-methods-e7dd64b2df45
url
https://medium.com/@aly.route/15-extension-methods-e7dd64b2df45
canonical_url
https://medium.com/@aly.route/15-extension-methods-e7dd64b2df45
author_url
https://medium.com/@aly.route
status
ok
fetched_at
2026-07-17 14:47:18