DEV Community

Ge Ji
Ge Ji

Posted on

Dart Lesson 5: function base - the smallest unit of code reuse

Today we're going to learn about functions - the smallest unit of code reuse in Dart. Functions are like "code modules" that encapsulate reusable logic, making programs more concise and maintainable.

I. Function Definition: Basic Structure and Conventions

A function is a block of code that performs a specific task. In Dart, defining a function requires specifying its return type, name, parameter list, and body.

1. Basic Syntax

returnType functionName(parameterList) {
  // Function body (code to execute)
  return returnValue; // Must match returnType
}
Enter fullscreen mode Exit fullscreen mode

2. Detailed Explanation

  • Return Type:
    • If a function returns a value, specify its type (e.g., int, String).
    • If it doesn't return a value, you can omit the type or specify void.
  • Function Name:
    • Follows camelCase naming convention (starts with lowercase, subsequent words start with uppercase), like calculateSum or printMessage.
    • Should clearly express the function's purpose; avoid vague names like doSomething.
  • Parameter List:
    • Consists of "type name" pairs, separated by commas.
    • Serves as the entry point for receiving external data.

Example 1: Function with Return Value

// Define a function that calculates the sum of two integers
int calculateSum(int a, int b) {
  int result = a + b;
  return result; // Returns calculation result (int type)
}

void main() {
  int sum = calculateSum(3, 5); // Call function with arguments 3 and 5
  print("Sum: $sum"); // Output: Sum: 8
}
Enter fullscreen mode Exit fullscreen mode

Example 2: Function with No Return Value (void)

// Define a function that prints a message
void printMessage(String name) {
  print("Hello, $name!"); // No return statement
}

void main() {
  printMessage("Dart"); // Call function with argument "Dart"
  // Output: Hello, Dart!
}
Enter fullscreen mode Exit fullscreen mode

3. Simplified Function Syntax

When a function body contains only one return statement, you can omit the braces and return keyword, using the arrow syntax => instead:

// Simplified sum function
int calculateSum(int a, int b) => a + b;

// Simplified print function (works for void functions too)
void printMessage(String name) => print("Hello, $name!");
Enter fullscreen mode Exit fullscreen mode

II. Optional Parameters: Making Functions More Flexible

In some scenarios, function parameters may not be required (like optional configurations). Dart provides two types of optional parameters: positional optional parameters and named optional parameters.

1. Positional Optional Parameters (wrapped in [])

  • Wrapped in square brackets [] and placed after required parameters.
  • Can be omitted when calling; if omitted, uses default value (or null if no default specified).
  • Arguments must correspond to parameter positions when passed.

Example: Positional Optional Parameters with Default Values

// Define a function to calculate total price:
// price (required) + count (optional, default 1) + discount (optional, default 0)
double calculateTotal(double price, [int count = 1, double discount = 0.0]) {
  double total = price * count * (1 - discount);
  return total;
}

void main() {
  // Case 1: Only required parameter (price)
  double total1 = calculateTotal(100.0);
  print("Total 1: $total1"); // Output: Total 1: 100.0 (count=1, no discount)

  // Case 2: Required parameter + first optional parameter (count)
  double total2 = calculateTotal(100.0, 2);
  print("Total 2: $total2"); // Output: Total 2: 200.0 (count=2, no discount)

  // Case 3: All parameters
  double total3 = calculateTotal(100.0, 2, 0.1);
  print("Total 3: $total3"); // Output: Total 3: 180.0 (count=2, 10% discount)
}
Enter fullscreen mode Exit fullscreen mode

2. Named Optional Parameters (wrapped in {})

  • Wrapped in curly braces {}, usually with default values (otherwise may be null).
  • Must be passed using "parameterName: value" syntax when calling, order doesn't matter.
  • Better for functions with many parameters, improving code readability.

Example: Named Optional Parameters

// Define a function to create user information:
// name (required) + age (optional) + email (optional)
String createUser(String name, {int? age, String? email}) {
  String info = "Name: $name";
  if (age != null) info += ", Age: $age";
  if (email != null) info += ", Email: $email";
  return info;
}

void main() {
  // Case 1: Only required parameter
  String user1 = createUser("Zhang San");
  print(user1); // Output: Name: Zhang San

  // Case 2: Required parameter + email (skipping age)
  String user2 = createUser("Li Si", email: "lisi@example.com");
  print(user2); // Output: Name: Li Si, Email: lisi@example.com

  // Case 3: All parameters (order doesn't matter)
  String user3 = createUser("Wang Wu", age: 25, email: "wangwu@example.com");
  print(user3); // Output: Name: Wang Wu, Age: 25, Email: wangwu@example.com
}
Enter fullscreen mode Exit fullscreen mode

Tip: Named optional parameters are more commonly used in practice because explicit parameter names reduce errors.


III. Anonymous Functions: Functions Without Names

Anonymous functions are functions without a name, typically used to define simple, temporary logic passed as arguments to other functions.

1. Basic Syntax

(parameterList) {
  // Function body
};
// Or simplified (for single-line logic)
(parameterList) => expression;
Enter fullscreen mode Exit fullscreen mode

2. Common Usage: Passing as Arguments

Anonymous functions are frequently used with collection methods like forEach and map:

Example 1: Using Anonymous Functions with Collection Traversal

void main() {
  List<String> fruits = ['Apple', 'Banana', 'Orange'];

  // Use anonymous function as argument to forEach
  fruits.forEach((fruit) {
    print("Fruit: $fruit");
  });
  // Output:
  // Fruit: Apple
  // Fruit: Banana
  // Fruit: Orange
}
Enter fullscreen mode Exit fullscreen mode

Example 2: Temporary Calculation Logic

void main() {
  // Define a function that accepts another function as a parameter
  void calculateAndPrint(int a, int b, Function operation) {
    dynamic result = operation(a, b); // Call the passed function
    print("Result: $result");
  }

  // Pass anonymous function (addition)
  calculateAndPrint(5, 3, (x, y) => x + y); // Output: Result: 8

  // Pass another anonymous function (multiplication)
  calculateAndPrint(5, 3, (x, y) => x * y); // Output: Result: 15
}
Enter fullscreen mode Exit fullscreen mode

IV. Immediately Invoked Function Expressions (IIFEs)

IIFEs are functions that run immediately after definition, often used to isolate scope (preventing variable pollution).

1. Syntax and Examples

Add () with arguments immediately after function definition to execute it right away:

void main() {
  // Immediately invoked function (no parameters)
  (() {
    print("This function executes immediately!");
  })();
  // Output: This function executes immediately!

  // Immediately invoked function with parameters
  ((String message) {
    print("Message: $message");
  })("Hello, IIFE!");
  // Output: Message: Hello, IIFE!

  // Return value of IIFE can be captured
  int result = ((int a, int b) => a + b)(10, 20);
  print(
    "Immediately calculated sum: $result",
  ); // Output: Immediately calculated sum: 30
}
Enter fullscreen mode Exit fullscreen mode

2. Purpose: Isolating Variable Scope

Variables inside an IIFE don't affect the outer scope:

void main() {
  // Outer variable
  int x = 10;

  // Immediately invoked function
  (() {
    int x = 20; // Inner variable, independent of outer x
    print("Inner x: $x"); // Output: Inner x: 20
  })();

  print("Outer x: $x"); // Output: Outer x: 10 (unaffected by inner variable)
}
Enter fullscreen mode Exit fullscreen mode

V. Other Function Features

1. Functions as Return Values

Functions can return other functions, common in higher-order functions:

// Define a function that returns a function
Function makeAdder(int increment) {
  return (int number) => number + increment; // Return an anonymous function
}

void main() {
  // Get a function that "adds 5"
  var add5 = makeAdder(5);
  print(add5(10)); // Output: 15 (10 + 5)

  // Get a function that "adds 10"
  var add10 = makeAdder(10);
  print(add10(10)); // Output: 20 (10 + 10)
}
Enter fullscreen mode Exit fullscreen mode

2. Type Restrictions for Function Parameters

For better type safety, you can specify types for function parameters (e.g., int Function(int, int) denotes a function that takes two ints and returns an int):

// Define a function that accepts a specifically typed function
int calculate(int a, int b, int Function(int, int) operation) {
  return operation(a, b);
}

void main() {
  // Pass a function matching the type requirement (addition)
  int sum = calculate(3, 4, (x, y) => x + y);
  print(sum); // Output: 7

  // Pass a multiplication function
  int product = calculate(3, 4, (x, y) => x * y);
  print(product); // Output: 12
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)