Для чего extension нужен

В языке Dart extension — это механизм, позволяющий расширять существующие классы дополнительными методами, геттерами, сеттерами без необходимости наследования или модификации оригинального класса. Он особенно полезен, когда нужно добавить вспомогательные функции к уже существующим типам, включая стандартные типы вроде String, int, List, а также сторонние или даже финальные классы.

📌 Основные цели использования extension

1. Добавление новых методов к существующим классам

Позволяет добавить удобные утилиты или читаемые сокращения без изменения оригинального класса.

extension StringExtensions on String {
bool get isValidEmail => RegExp(r"^\[\\w\\.-\]+@\[\\w\\.-\]+\\.\\w+$").hasMatch(this);
}

Теперь можно писать:

'example@gmail.com'.isValidEmail; // true

2. Повышение читаемости и выразительности кода

extension позволяет писать более выразительный и «естественный» код:

final distance = point1.distanceTo(point2); // вместо Utility.distance(point1, point2)

3. Избежание утилитарных классов

Вместо создания классов со статическими методами вроде Utils.capitalize(String s) — можно использовать расширения, и вызывать метод напрямую у объекта.

extension CapExtension on String {
String capitalize() => this\[0\].toUpperCase() + substring(1);
}
'flutter'.capitalize(); // 'Flutter'

4. Расширение сторонних библиотек

Если класс не находится под вашим контролем (например, DateTime, BuildContext, HttpClient, TextEditingController), но вы хотите добавить к нему удобные методы — extension идеально подходит.

extension DateHelpers on DateTime {
bool get isWeekend => weekday == DateTime.saturday || weekday == DateTime.sunday;
}

5. Реализация DSL (Domain-Specific Language)

Можно создать удобные интерфейсы или синтаксический сахар для читаемого API, особенно при работе с Flutter:

extension PaddingExtension on Widget {
Widget get padded => Padding(padding: const EdgeInsets.all(8.0), child: this);
}

Использование:

Text('Hello').padded

🛠 Синтаксис и правила

Объявление расширения:

extension ExtensionName on ExistingType {
// методы
// геттеры
// сеттеры
}

Использование без имени:

Можно создать анонимное расширение, если не нужно вызывать по имени в случае конфликта:

extension on int {
int timesTwo() => this \* 2;
}

С параметрами типа:

Расширение можно сделать обобщённым (generic):

extension ListUtils<T> on List<T> {
void printAll() {
forEach((element) => print(element));
}
}

⚠️ Ограничения

  • Расширения не могут добавлять состояние (переменные экземпляра).

  • Нельзя переопределять существующие методы.

  • Если есть несколько расширений с одинаковыми именами методов, используется явное указание имени расширения.

  • Расширения не могут заменять интерфейсы или наследование: они не участвуют в механизме динамического связывания.

// При конфликте
String text = 'example';
StringExtensions(text).capitalize();

📦 Практические сценарии использования

Flutter:

extension BuildContextExtensions on BuildContext {
void showSnackBar(String message) {
ScaffoldMessenger.of(this).showSnackBar(
SnackBar(content: Text(message)),
);
}
}

Работа со списками:

extension FirstWhereOrNullExtension<T> on Iterable<T> {
T? firstWhereOrNull(bool Function(T) test) {
for (var element in this) {
if (test(element)) return element;
}
return null;
}
}

Парсинг чисел:

extension NumParsing on String {
int toIntOrZero() => int.tryParse(this) ?? 0;
}

📚 Где применяются чаще всего

  • Расширения для String, List, Map, DateTime

  • Упрощение работы с BuildContext, Theme, MediaQuery, Navigator

  • Поведение в Form, Validation

  • Добавление логирования (.log(), .debug() и пр.)

  • Бизнес-логика в виде DSL

extension в Dart — это мощный способ улучшения читаемости, поддержки и повторного использования кода без необходимости вмешательства в исходные определения классов. Особенно активно применяется во Flutter-разработке для организации удобных и выразительных API.