Arrow Functions & Lambdas
Arrow Functions & Lambdas
In Dart, functions are first-class citizens — you can assign them to variables, pass them as arguments, and return them from other functions. Arrow functions and anonymous functions (lambdas) make working with functions concise and expressive.
1. Arrow Functions — The => Syntax
An arrow function is a shorthand for a function that contains only a single expression. Instead of writing a full function body with {} and return, you use =>.
Syntax:
dartreturnType functionName(params) => expression;
Regular Function vs Arrow Function
dart// ✅ Regular function int add(int a, int b) { return a + b; } // ✅ Arrow function — same thing, fewer lines int add(int a, int b) => a + b; void main() { print(add(3, 4)); // 7 }
More Examples
dart// Returns a greeting string String greet(String name) => 'Hello, $name!'; // Checks if a number is even bool isEven(int n) => n % 2 == 0; // Prints a message (void arrow function) void sayHi() => print('Hi there!'); void main() { print(greet('Rahul')); // Hello, Rahul! print(isEven(10)); // true sayHi(); // Hi there! }
When to use =>: Only when the function body is a single expression. If you need multiple statements, if-else blocks, or loops — use the regular {} syntax.
2. Anonymous Functions (Function Literals / Lambdas)
An anonymous function is a function without a name. It is defined inline and often passed directly as an argument or assigned to a variable.
dart// Named function int square(int x) { return x * x; } // Anonymous function assigned to a variable var square = (int x) { return x * x; }; // Arrow-style anonymous function var square = (int x) => x * x; void main() { print(square(5)); // 25 }
Anonymous Functions as Arguments
Anonymous functions shine when passed directly into other functions:
dartvoid main() { List<String> fruits = ['mango', 'apple', 'banana']; // Passing an anonymous function to forEach fruits.forEach((fruit) { print('I love $fruit'); }); // Arrow-style — even more concise fruits.forEach((fruit) => print('Fruit: $fruit')); }
Output:
I love mango
I love apple
I love banana
Fruit: mango
Fruit: apple
Fruit: banana
3. Closures — Capturing Variables from Outer Scope
A closure is a function that "closes over" variables from its surrounding scope — it remembers and can access those variables even after the outer function has finished executing.
dartFunction makeCounter() { int count = 0; // outer variable return () { count++; // inner function captures 'count' print('Count: $count'); }; } void main() { var counter = makeCounter(); counter(); // Count: 1 counter(); // Count: 2 counter(); // Count: 3 // A new, independent counter var counter2 = makeCounter(); counter2(); // Count: 1 }
Each call to makeCounter() creates a new closure with its own count variable.
Practical Closure Example — Multiplier Factory
dartFunction multiplier(int factor) { return (int number) => number * factor; } void main() { var doubleIt = multiplier(2); var tripleIt = multiplier(3); print(doubleIt(5)); // 10 print(tripleIt(5)); // 15 print(doubleIt(8)); // 16 }
4. Assigning Functions to Variables — Function Type
In Dart, the Function type lets you store any function in a variable:
dartvoid main() { // Using 'Function' type (loose typing) Function greet = (String name) => 'Hello, $name!'; print(greet('Priya')); // Hello, Priya! // More specific type annotation String Function(String) greet2 = (name) => 'Hi, $name!'; print(greet2('Amit')); // Hi, Amit! // Function that takes int, returns bool bool Function(int) isPositive = (n) => n > 0; print(isPositive(5)); // true print(isPositive(-3)); // false }
Passing Functions as Arguments
dartvoid applyOperation(int a, int b, int Function(int, int) operation) { int result = operation(a, b); print('Result: $result'); } void main() { applyOperation(10, 5, (a, b) => a + b); // Result: 15 applyOperation(10, 5, (a, b) => a * b); // Result: 50 applyOperation(10, 5, (a, b) => a - b); // Result: 5 }
5. typedef — Function Type Aliases
typedef lets you create a named alias for a function signature, making your code more readable and reusable.
dart// Define type aliases for function signatures typedef MathOperation = int Function(int, int); typedef Validator = bool Function(String); typedef Callback = void Function(String message); // Use typedef as a parameter type void calculate(int a, int b, MathOperation op) { print('Result: ${op(a, b)}'); } bool validateEmail(String email, Validator validator) { return validator(email); } void main() { MathOperation add = (a, b) => a + b; MathOperation multiply = (a, b) => a * b; calculate(4, 3, add); // Result: 7 calculate(4, 3, multiply); // Result: 12 Validator emailCheck = (email) => email.contains('@'); print(validateEmail('user@example.com', emailCheck)); // true print(validateEmail('notanemail', emailCheck)); // false }
Tip: Use typedef when you reuse the same function signature in multiple places. It acts like a contract — anyone using the typedef knows exactly what arguments and return type to expect.
6. Practical Examples
Sorting with a Comparator
dartvoid main() { List<String> names = ['Zara', 'Amit', 'Priya', 'Rahul']; // Sort alphabetically names.sort((a, b) => a.compareTo(b)); print(names); // [Amit, Priya, Rahul, Zara] // Sort by string length names.sort((a, b) => a.length.compareTo(b.length)); print(names); // [Amit, Zara, Priya, Rahul] List<int> scores = [45, 92, 78, 56, 88]; // Sort descending scores.sort((a, b) => b.compareTo(a)); print(scores); // [92, 88, 78, 56, 45] }
Callbacks
darttypedef OnComplete = void Function(String result); void fetchData(String url, OnComplete onComplete) { print('Fetching from $url...'); String data = '{"name": "Dart", "version": "3.0"}'; onComplete(data); } void main() { fetchData('https://api.example.com/data', (result) { print('Data received: $result'); }); }
Output:
Fetching from https://api.example.com/data...
Data received: {"name": "Dart", "version": "3.0"}
Converting Regular Functions to Arrow Syntax
dart// Before — regular functions bool isAdult(int age) { return age >= 18; } String formatName(String first, String last) { return '$first $last'; } double calculateArea(double radius) { return 3.14159 * radius * radius; } // After — arrow functions (cleaner!) bool isAdult(int age) => age >= 18; String formatName(String first, String last) => '$first $last'; double calculateArea(double radius) => 3.14159 * radius * radius; void main() { print(isAdult(20)); // true print(isAdult(15)); // false print(formatName('Raj', 'Shah')); // Raj Shah print(calculateArea(5.0)); // 78.53975 }
Summary
| Concept | Description | Example |
|---|---|---|
Arrow Function => | Single-expression shorthand | int add(a, b) => a + b; |
| Anonymous Function | Nameless function literal | (x) => x * 2 |
| Closure | Function capturing outer variables | makeCounter() pattern |
Function type | Storing functions in variables | Function fn = () => ...; |
typedef | Named alias for function signature | typedef Op = int Function(int, int); |
Arrow functions and anonymous functions are everywhere in Dart — especially when working with collections, callbacks, and Flutter event handlers. Mastering them is key to writing clean, idiomatic Dart code.