Extension Methods
Part of the Object Oriented Programming section of Coddy's Dart journey — lesson 83 of 110.
Sometimes you want to add functionality to a class you don't own - like Dart's built-in String or int types. You can't modify their source code, and inheritance doesn't help with final classes. This is where extension methods come in.
Extensions let you add new methods to existing types without modifying them or creating subclasses:
extension StringExtras on String {
String reverse() {
return split('').reversed.join('');
}
bool get isPalindrome {
var cleaned = toLowerCase().replaceAll(' ', '');
return cleaned == cleaned.split('').reversed.join('');
}
}
void main() {
print('hello'.reverse()); // olleh
print('radar'.isPalindrome); // true
}The syntax is extension ExtensionName on Type. Inside, you define methods and getters just like in a regular class. Once defined, these methods appear on all instances of that type as if they were built-in.
Extensions work on any type, including your own classes and generic types:
extension IntExtras on int {
int squared() => this * this;
}
extension ListExtras<T> on List<T> {
T? get secondOrNull => length >= 2 ? this[1] : null;
}
void main() {
print(5.squared()); // 25
print([1, 2, 3].secondOrNull); // 2
}Notice that inside an extension, this refers to the instance the method is called on. Extensions are a clean way to enhance existing types while keeping your code organized and readable.
Challenge
EasyLet's enhance Dart's built-in types with useful functionality using extension methods! You'll create extensions that add helpful methods to String and List types, then use them in your main file.
You'll organize your code into two files:
extensions.dart: Create two extensions that add new capabilities to existing types:- An extension called
StringUtilsonStringwith:- A method
countVowels()that returns the number of vowels (a, e, i, o, u - case insensitive) in the string - A getter
initialsthat returns the first letter of each word (split by spaces), joined together and uppercased. For example,"hello world"becomes"HW"
- A method
- An extension called
ListUtils<T>onList<T>with:- A method
safeGet(int index)that returns the element at the index if it exists, ornullif the index is out of bounds - A getter
hasMultiplethat returnstrueif the list has more than one element
- A method
- An extension called
main.dart: Import your extensions and demonstrate them in action:- Create a string
"Object Oriented Programming"and print its vowel count in the format:Vowels: [count] - Print the initials of the same string in the format:
Initials: [initials] - Create a list of integers
[10, 20, 30] - Print the result of
safeGet(1)in the format:Element at 1: [value] - Print the result of
safeGet(5)in the format:Element at 5: [value] - Print whether the list has multiple elements in the format:
Has multiple: [true/false]
- Create a string
Remember that inside an extension, this refers to the instance the method is called on. Your extensions will make these types feel like they have these methods built-in!
Expected output:
Vowels: 9
Initials: OOP
Element at 1: 20
Element at 5: null
Has multiple: trueCheat sheet
Extension methods allow you to add new functionality to existing types without modifying their source code or using inheritance.
The syntax for creating an extension is extension ExtensionName on Type:
extension StringExtras on String {
String reverse() {
return split('').reversed.join('');
}
bool get isPalindrome {
var cleaned = toLowerCase().replaceAll(' ', '');
return cleaned == cleaned.split('').reversed.join('');
}
}Once defined, extension methods can be called on instances of that type as if they were built-in:
print('hello'.reverse()); // olleh
print('radar'.isPalindrome); // trueExtensions work on any type, including built-in types, your own classes, and generic types:
extension IntExtras on int {
int squared() => this * this;
}
extension ListExtras<T> on List<T> {
T? get secondOrNull => length >= 2 ? this[1] : null;
}
print(5.squared()); // 25
print([1, 2, 3].secondOrNull); // 2Inside an extension, this refers to the instance the method is called on.
Try it yourself
import 'extensions.dart';
void main() {
// Create the string
String text = "Object Oriented Programming";
// TODO: Print the vowel count using countVowels()
// Format: Vowels: [count]
// TODO: Print the initials using the initials getter
// Format: Initials: [initials]
// Create the list of integers
List<int> numbers = [10, 20, 30];
// TODO: Print the result of safeGet(1)
// Format: Element at 1: [value]
// TODO: Print the result of safeGet(5)
// Format: Element at 5: [value]
// TODO: Print whether the list has multiple elements
// Format: Has multiple: [true/false]
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesLibraries & ImportsIntroduction to OOPClasses vs ObjectsThe this KeywordMethodsInstance VariablesConstructor BasicsRecap - Simple Calculator4Null Safety
Intro to Null SafetyNullable vs Non-NullableThe ? and ! OperatorsLate Keyword & Null SafetyNull-Aware OperatorsNull Safety in ClassesRecap - User Profile System7Abstract Classes & Interfaces
Abstract ClassesAbstract MethodsInterfaces in DartImplicit InterfacesImplementing vs ExtendingMultiple InterfacesRecap - Shape Calculator10Collections & Generics
List, Set, Map OverviewType-Safe CollectionsGeneric ClassesGeneric MethodsGeneric ConstraintsIterable & IteratorRecap - Generic Storage13Advanced OOP Concepts
Composition vs InheritanceExtension MethodsCallable ClassesSealed Classes (Dart 3)Records (Dart 3)Patterns & Matching (3.0)Enums with Methods16Project: Library Management
Project OverviewBook and User Classes2Constructors in Dart
Default ConstructorNamed ConstructorsInitializer ListsConstant ConstructorsFactory ConstructorsRedirecting ConstructorsRecap - Shape Builder5Encapsulation
Public vs Private MembersThe _ Prefix ConventionLibrary-Level PrivacyGetters & Setters DepthInformation HidingRecap - Student Records8Mixins
Introduction to MixinsCreating MixinsUsing Multiple Mixinson Keyword in MixinsMixin vs InheritanceMixin vs InterfaceRecap - Animal System11Special Methods
toString() OverridehashCode & == OverrideComparable Interfacecall() MethodnoSuchMethod OverrideRecap - Custom Collection14Design Patterns Part 1
Intro to Design PatternsSingleton PatternFactory PatternObserver PatternStrategy Pattern3Class Properties
Instance vs Static MembersFinal & Const FieldsLate VariablesStatic Methods & FieldsGetters and SettersRecap - Bank Account Manager6Inheritance
Basic InheritanceThe super KeywordMethod OverridingThe @override AnnotationThe final Class KeywordConstructors & InheritanceRecap - Employee Hierarchy9Polymorphism
Polymorphism BasicsPolymorphism via InterfacesRuntime Type CheckingThe is & as OperatorsCovariant KeywordRecap - Payment Processor