Home
Dart
OOP
Flutter
Contact

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

TypeSyntaxDescription
Required positionalf(int a)Must be provided in order
Optional positionalf(int a, [int b])Can be omitted
Namedf({int a})Passed by name
Required namedf({required int a})Must be provided by name
Default valuef({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, switch with pattern matching, and all standard loop types
  • Switch statements support when guards 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.
K
Kesavaraja Murugesan
Flutter Developer & Educator

Flutter developer with 3 years of experience building production-grade mobile applications. Passionate about teaching clean architecture patterns and helping developers write maintainable, scalable code.

← Variables & Types Next: Coding Challenges →