Answer
Overview
Extension methods in Dart allow you to add new functionality to existing classes without modifying their source code or creating subclasses. They're particularly useful for adding helper methods to built-in types or third-party classes.
Syntax
dartextension ExtensionName on TargetType { // Add methods, getters, setters, operators }
Basic Examples
String Extensions
dartextension StringExtensions on String { // Capitalize first letter String get capitalize { if (isEmpty) return this; return '${this[0].toUpperCase()}${substring(1)}'; } // Check if email bool get isEmail { return RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(this); } // Reverse string String get reverse { return split('').reversed.join(''); } // Remove whitespace String get removeWhitespace { return replaceAll(RegExp(r'\s+'), ''); } } // Usage void main() { print('hello'.capitalize); // Hello print('test@email.com'.isEmail); // true print('flutter'.reverse); // rettulf print('hello world'.removeWhitespace); // helloworld }
Number Extensions
dartextension IntExtensions on int { // Check if even bool get isEven => this % 2 == 0; // Check if odd bool get isOdd => this % 2 != 0; // Convert to currency format String get toCurrency => '\$${toStringAsFixed(2)}'; // Create list of range List<int> to(int end) { return List.generate(end - this + 1, (i) => this + i); } } extension DoubleExtensions on double { // Convert to percentage String get toPercentage => '${(this * 100).toStringAsFixed(1)}%'; } // Usage void main() { print(5.isEven); // false print(4.isOdd); // false print(1500.toCurrency); // $1500.00 print(1.to(5)); // [1, 2, 3, 4, 5] print(0.75.toPercentage); // 75.0% }
DateTime Extensions
dartextension DateTimeExtensions on DateTime { // Check if today bool get isToday { final now = DateTime.now(); return year == now.year && month == now.month && day == now.day; } // Check if yesterday bool get isYesterday { final yesterday = DateTime.now().subtract(Duration(days: 1)); return year == yesterday.year && month == yesterday.month && day == yesterday.day; } // Format as string String get formatted { return '${day.toString().padLeft(2, '0')}/' '${month.toString().padLeft(2, '0')}/$year'; } // Add days DateTime addDays(int days) { return add(Duration(days: days)); } } // Usage void main() { final now = DateTime.now(); print(now.isToday); // true print(now.formatted); // 10/03/2026 print(now.addDays(5).formatted); // 15/03/2026 }
Flutter-Specific Extensions
BuildContext Extensions
dartextension BuildContextExtensions on BuildContext { // Easy access to theme ThemeData get theme => Theme.of(this); // Easy access to text theme TextTheme get textTheme => Theme.of(this).textTheme; // Easy access to color scheme ColorScheme get colorScheme => Theme.of(this).colorScheme; // Screen size Size get screenSize => MediaQuery.of(this).size; // Screen width double get screenWidth => MediaQuery.of(this).size.width; // Screen height double get screenHeight => MediaQuery.of(this).size.height; // Is mobile bool get isMobile => screenWidth < 600; // Is tablet bool get isTablet => screenWidth >= 600 && screenWidth < 1200; // Is desktop bool get isDesktop => screenWidth >= 1200; // Show snackbar void showSnackBar(String message) { ScaffoldMessenger.of(this).showSnackBar( SnackBar(content: Text(message)), ); } // Navigate Future<T?> push<T>(Widget page) { return Navigator.push( this, MaterialPageRoute(builder: (_) => page), ); } // Pop void pop<T>([T? result]) { Navigator.pop(this, result); } } // Usage class MyWidget extends StatelessWidget { Widget build(BuildContext context) { return Container( width: context.screenWidth * 0.8, child: Text( 'Hello', style: context.textTheme.headlineMedium, ), ); } } // Show snackbar context.showSnackBar('Success!'); // Navigate context.push(DetailsScreen()); // Check device type if (context.isMobile) { // Mobile layout } else if (context.isTablet) { // Tablet layout }
Widget Extensions
dartextension WidgetExtensions on Widget { // Add padding Widget padding(EdgeInsets insets) { return Padding(padding: insets, child: this); } // Add margin (using Container) Widget margin(EdgeInsets insets) { return Container(margin: insets, child: this); } // Center widget Widget get center { return Center(child: this); } // Expanded widget Widget get expanded { return Expanded(child: this); } // Flexible widget Widget flexible({int flex = 1}) { return Flexible(flex: flex, child: this); } // Tap gesture Widget onTap(VoidCallback onTap) { return GestureDetector(onTap: onTap, child: this); } // Visibility Widget visible(bool isVisible) { return Visibility(visible: isVisible, child: this); } } // Usage Text('Hello') .padding(EdgeInsets.all(16)) .onTap(() => print('Tapped')) .center; Container(child: Text('Expand')) .expanded; Icon(Icons.star) .visible(showStar);
List Extensions
dartextension ListExtensions<T> on List<T> { // Get first or null T? get firstOrNull { return isEmpty ? null : first; } // Get last or null T? get lastOrNull { return isEmpty ? null : last; } // Separate with divider List<Widget> separatedBy(Widget separator) { if (isEmpty) return []; return [ for (int i = 0; i < length; i++) ...[ this[i] as Widget, if (i < length - 1) separator, ], ]; } } // Usage final numbers = [1, 2, 3]; print(numbers.firstOrNull); // 1 final emptyList = <int>[]; print(emptyList.firstOrNull); // null // Separate widgets Column( children: [ Text('Item 1'), Text('Item 2'), Text('Item 3'), ].separatedBy(Divider()), );
Complete Example
dartimport 'package:flutter/material.dart'; // String extensions extension StringX on String { String get capitalize => isEmpty ? this : '${this[0].toUpperCase()}${substring(1)}'; bool get isEmail => RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(this); } // Context extensions extension ContextX on BuildContext { double get width => MediaQuery.of(this).size.width; double get height => MediaQuery.of(this).size.height; void showSnack(String msg) { ScaffoldMessenger.of(this).showSnackBar(SnackBar(content: Text(msg))); } } // Widget extensions extension WidgetX on Widget { Widget p(double value) => Padding(padding: EdgeInsets.all(value), child: this); Widget get center => Center(child: this); } class ExampleScreen extends StatelessWidget { Widget build(BuildContext context) { final email = 'test@example.com'; return Scaffold( appBar: AppBar(title: Text('extension demo'.capitalize)), body: Column( children: [ Text('Valid email: ${email.isEmail}').p(16), ElevatedButton( onPressed: () => context.showSnack('Extension works!'), child: Text('Show Snackbar'), ).center, Text('Width: ${context.width}').p(8), ], ), ); } }
Generic Extensions
dartextension IterableExtensions<T> on Iterable<T> { // Map with index Iterable<R> mapIndexed<R>(R Function(int index, T item) f) { int index = 0; return map((item) => f(index++, item)); } // Group by Map<K, List<T>> groupBy<K>(K Function(T) keyFn) { final map = <K, List<T>>{}; for (var item in this) { final key = keyFn(item); (map[key] ??= []).add(item); } return map; } } // Usage final items = ['apple', 'banana', 'apricot', 'berry']; final grouped = items.groupBy((item) => item[0]); // {a: [apple, apricot], b: [banana, berry]} final indexed = items.mapIndexed((index, item) => '$index: $item'); // [0: apple, 1: banana, 2: apricot, 3: berry]
Best Practices
Tip: Use extensions to add convenience methods, not complex logic
✅ Do
dart// Good - Simple, reusable helpers extension StringHelpers on String { String get capitalize => /* simple logic */; bool get isEmail => /* validation */; }
❌ Don't
dart// Bad - Complex business logic in extension extension UserExtensions on User { Future<void> saveToDatabase() async { // Complex logic - should be in a service } }
Name Extensions Clearly
dart// ✅ Good extension StringFormatting on String {} extension ContextHelpers on BuildContext {} // ❌ Bad extension Utils on String {} // Too generic extension X on BuildContext {} // Not descriptive