Skip to content

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 Doe

Optional 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. Bob

Named 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 matter

Required 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 required

Default 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

  1. Use arrow syntax for simple one-line functions
  2. Prefer named parameters for functions with multiple parameters
  3. Use required for mandatory named parameters
  4. Keep functions small and focused on a single task
  5. Use descriptive names that indicate what the function does
  6. 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)}');
}

Next Steps

Content is for learning and research only.