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
}
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
}
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!
}
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!");
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)
}
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
}
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;
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
}
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
}
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
}
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)
}
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)
}
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
}
Top comments (0)