Functions
Functions are reusable blocks of code that perform specific tasks. In Dart, functions are first-class objects, meaning they can be assigned to variables, passed as arguments, and returned from other functions.
Basic Function Syntax
dart
// Function with return type
int add(int a, int b) {
return a + b;
}
// Function without return value
void greet(String name) {
print('Hello, $name!');
}
// Arrow function (single expression)
int multiply(int a, int b) => a * b;
void main() {
print(add(5, 3)); // 8
greet('Alice'); // Hello, Alice!
print(multiply(4, 5)); // 20
}Parameters
Required Positional Parameters
dart
String fullName(String first, String last) {
return '$first $last';
}
print(fullName('John', 'Doe')); // John DoeOptional Positional Parameters
Use square brackets []:
dart
String greet(String name, [String? title]) {
if (title != null) {
return 'Hello, $title $name';
}
return 'Hello, $name';
}
print(greet('Alice')); // Hello, Alice
print(greet('Bob', 'Dr.')); // Hello, Dr. BobNamed Parameters
Use curly braces {}:
dart
void printInfo({String? name, int? age}) {
print('Name: $name, Age: $age');
}
printInfo(name: 'Alice', age: 25);
printInfo(age: 30, name: 'Bob'); // Order doesn't matterRequired Named Parameters
dart
void createUser({required String name, required String email}) {
print('User: $name, Email: $email');
}
createUser(name: 'Alice', email: 'alice@example.com');
// createUser(name: 'Bob'); // Error: email is requiredDefault Parameter Values
dart
String greet(String name, [String greeting = 'Hello']) {
return '$greeting, $name!';
}
print(greet('Alice')); // Hello, Alice!
print(greet('Bob', 'Hi')); // Hi, Bob!
// Named parameters with defaults
void printMessage({String message = 'Default', int times = 1}) {
for (int i = 0; i < times; i++) {
print(message);
}
}
printMessage(); // Default (once)
printMessage(message: 'Hi', times: 3); // Hi (three times)Return Values
dart
// Explicit return
int square(int n) {
return n * n;
}
// Arrow function
int cube(int n) => n * n * n;
// Multiple return values using records (Dart 3.0+)
(int, int) minMax(List<int> numbers) {
return (numbers.reduce((a, b) => a < b ? a : b),
numbers.reduce((a, b) => a > b ? a : b));
}
void main() {
var (min, max) = minMax([3, 1, 4, 1, 5, 9]);
print('Min: $min, Max: $max'); // Min: 1, Max: 9
}Anonymous Functions (Lambdas)
dart
// Anonymous function
var multiply = (int a, int b) {
return a * b;
};
// Arrow function
var add = (int a, int b) => a + b;
// Using with collections
List<int> numbers = [1, 2, 3, 4, 5];
numbers.forEach((num) {
print(num * 2);
});
var doubled = numbers.map((num) => num * 2).toList();
print(doubled); // [2, 4, 6, 8, 10]Higher-Order Functions
Functions that take other functions as parameters or return functions:
dart
// Function as parameter
void executeOperation(int a, int b, int Function(int, int) operation) {
print('Result: ${operation(a, b)}');
}
int add(int a, int b) => a + b;
int multiply(int a, int b) => a * b;
void main() {
executeOperation(5, 3, add); // Result: 8
executeOperation(5, 3, multiply); // Result: 15
// With anonymous function
executeOperation(5, 3, (a, b) => a - b); // Result: 2
}
// Function returning function
Function makeMultiplier(int factor) {
return (int value) => value * factor;
}
void main() {
var double = makeMultiplier(2);
var triple = makeMultiplier(3);
print(double(5)); // 10
print(triple(5)); // 15
}Closures
Functions that capture variables from their surrounding scope:
dart
Function makeCounter() {
int count = 0;
return () {
count++;
return count;
};
}
void main() {
var counter = makeCounter();
print(counter()); // 1
print(counter()); // 2
print(counter()); // 3
}Recursive Functions
dart
// Factorial
int factorial(int n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
// Fibonacci
int fibonacci(int n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
void main() {
print(factorial(5)); // 120
print(fibonacci(7)); // 13
}Generator Functions
Synchronous Generator
Returns an Iterable:
dart
Iterable<int> countTo(int max) sync* {
for (int i = 1; i <= max; i++) {
yield i;
}
}
void main() {
for (var num in countTo(5)) {
print(num); // 1, 2, 3, 4, 5
}
}Asynchronous Generator
Returns a Stream:
dart
Stream<int> countStream(int max) async* {
for (int i = 1; i <= max; i++) {
await Future.delayed(Duration(seconds: 1));
yield i;
}
}
void main() async {
await for (var num in countStream(3)) {
print(num); // 1 (after 1s), 2 (after 2s), 3 (after 3s)
}
}Function Types
dart
// Type definition
typedef MathOperation = int Function(int, int);
int add(int a, int b) => a + b;
int subtract(int a, int b) => a - b;
void calculate(int x, int y, MathOperation operation) {
print(operation(x, y));
}
void main() {
calculate(10, 5, add); // 15
calculate(10, 5, subtract); // 5
}Best Practices
- Use arrow syntax for simple one-line functions
- Prefer named parameters for functions with multiple parameters
- Use required for mandatory named parameters
- Keep functions small and focused on a single task
- Use descriptive names that indicate what the function does
- Document complex functions with comments
Complete Example
dart
// Calculator with various function types
typedef Operation = double Function(double, double);
// Basic operations
double add(double a, double b) => a + b;
double subtract(double a, double b) => a - b;
double multiply(double a, double b) => a * b;
double divide(double a, double b) => b != 0 ? a / b : 0;
// Higher-order function
void performCalculation({
required double x,
required double y,
required Operation operation,
String? label,
}) {
double result = operation(x, y);
String operationName = label ?? 'Operation';
print('$operationName: $x and $y = $result');
}
// Function returning function
Operation getOperation(String type) {
return switch (type) {
'add' => add,
'subtract' => subtract,
'multiply' => multiply,
'divide' => divide,
_ => (a, b) => 0,
};
}
void main() {
double a = 10, b = 5;
performCalculation(x: a, y: b, operation: add, label: 'Addition');
performCalculation(x: a, y: b, operation: subtract, label: 'Subtraction');
performCalculation(x: a, y: b, operation: multiply, label: 'Multiplication');
performCalculation(x: a, y: b, operation: divide, label: 'Division');
// Using function factory
var op = getOperation('multiply');
print('Dynamic operation: ${op(a, b)}');
}