Control Flow & Functions
Master conditionals, loops, switch-case patterns, and Dart's flexible function syntax.
If-Else Statements
Dart supports standard conditional expressions with a few enhancements:
conditionals.dart
var temperature = 28;
if (temperature > 30) {
print('It\'s hot outside!');
} else if (temperature > 20) {
print('Nice weather!');
} else {
print('Stay warm!');
}
// Ternary operator
var status = temperature > 25 ? 'warm' : 'cool';
// Null-aware operator
String? nullableName;
var displayName = nullableName ?? 'Guest';
Switch-Case & Patterns
Dart 3 introduces powerful pattern matching in switch statements:
switch_patterns.dart
var command = 'activate';
switch (command) {
case 'activate':
print('System activated');
case 'deactivate':
print('System deactivated');
case 'status':
print('Showing status...');
default:
print('Unknown command');
}
// Pattern matching with when clause
var score = 85;
switch (score) {
case final s when s >= 90:
print('Grade: A');
case final s when s >= 80:
print('Grade: B');
case final s when s >= 70:
print('Grade: C');
default:
print('Grade: F');
}
// Destructuring with switch
List<int> point = [3, 4];
switch (point) {
case [0, 0]:
print('Origin');
case [var x, 0]:
print('On x-axis at $x');
case [0, var y]:
print('On y-axis at $y');
case [var x, var y]:
print('Point at ($x, $y)');
}
Loops
loops.dart
// Classic for loop
for (var i = 0; i < 5; i++) {
print('Index: $i');
}
// For-in loop
var colors = ['red', 'green', 'blue'];
for (var color in colors) {
print('Color: $color');
}
// While loop
var count = 0;
while (count < 3) {
print('Count: $count');
count++;
}
// Do-while loop
do {
print('Runs at least once');
} while (false);
Higher-Order Functions
Dart treats functions as first-class citizens. You can pass functions as arguments, return them from other functions, and store them in variables:
higher_order.dart
// Function as parameter
void processList(List<int> items, Function processor) {
for (var item in items) {
print(processor(item));
}
}
// Passing functions
processList([1, 2, 3], (n) => n * 2); // 2, 4, 6
processList([1, 2, 3], (n) => n * n); // 1, 4, 9
// Returning a function (closure)
Function multiplier(int factor) {
return (n) => n * factor;
}
var double = multiplier(2);
var triple = multiplier(3);
print(double(5)); // 10
print(triple(5)); // 15
This functional programming style is widely used in Flutter for building widget trees, handling events, and transforming data streams. Understanding closures is essential for writing idiomatic Dart.
📐 Real-World Pattern: Strategy Pattern
Higher-order functions enable the Strategy design pattern without classes. In a payment system, you might pass different validation functions based on payment type — each with its own rules but sharing the same processing pipeline.
Functions
functions.dart
// Basic function
int add(int a, int b) {
return a + b;
}
// Named parameters
void greet({required String name, String greeting = 'Hello'}) {
print('$greeting, $name!');
}
greet(name: 'Alice'); // Hello, Alice!
greet(name: 'Bob', greeting: 'Hi'); // Hi, Bob!
// Optional positional parameters
String formatName(String first, [String? last]) {
return last != null ? '$first $last' : first;
}
// First-class functions
var multiply = (int a, int b) => a * b;
print(multiply(3, 4)); // 12
Arrow Syntax
Concise syntax for single-expression functions:
arrow_syntax.dart
// Arrow functions (expression body)
int square(int n) => n * n;
bool isEven(int n) => n % 2 == 0;
// Used with collection methods
var numbers = [1, 2, 3, 4, 5];
var squares = numbers.map((n) => n * n).toList();
var evens = numbers.where((n) => n % 2 == 0).toList();
print(squares); // [1, 4, 9, 16, 25]
print(evens); // [2, 4]
Parameter Types
| Type | Syntax | Description |
|---|---|---|
| Required positional | f(int a) | Must be provided in order |
| Optional positional | f(int a, [int b]) | Can be omitted |
| Named | f({int a}) | Passed by name |
| Required named | f({required int a}) | Must be provided by name |
| Default value | f({int a = 0}) | Uses default if omitted |
Real-World Example: Permission Validation
Simulating user permission rules for a mobile app:
permissions.dart
enum Role { admin, editor, viewer }
Set<String> getPermissions(Role role) {
switch (role) {
case Role.admin:
return {'read', 'write', 'delete', 'manage_users'};
case Role.editor:
return {'read', 'write'};
case Role.viewer:
return {'read'};
}
}
bool hasPermission(Role role, String action) {
return getPermissions(role).contains(action);
}
void main() {
var roles = Role.values;
for (var role in roles) {
print('${role.name}: ${getPermissions(role)}');
var canDelete = hasPermission(role, 'delete');
print(' Can delete? $canDelete');
}
}
Summary
- Dart supports
if/else,switchwith pattern matching, and all standard loop types - Switch statements support
whenguards and destructuring - Functions support named, positional, and optional parameters
- Arrow syntax (
=>) provides concise single-expression functions
🚀 Next Step
Continue to OOP: Classes & Objects to start building with object-oriented programming.