1. Basic if / if-else Branching
- The content inside
ifparentheses must be a boolean expression; implicit coercion to boolean values is not allowed. - Multiple
else ifblocks are supported, with an optional finalelseblock. - Curly braces can be omitted for single-line logic, yet consistent use of braces is recommended to boost readability.
void main() {
bool isRaining = false;
bool isSnowing = true;
if (isRaining) {
print("Bring raincoat");
} else if (isSnowing) {
print("Wear thick coat");
} else {
print("Drive convertible");
}
}Code language: Dart (dart)
Execution Output
D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Wear thick coatCode language: CSS (css)
You can also use if alone
void main() {
bool isRaining = false;
bool isSnowing = true;
if (isRaining) {
print("Bring raincoat");
}
}Code language: Dart (dart)
Or simply use if else
void main() {
bool isRaining = false;
bool isSnowing = true;
if (isRaining) {
print("Bring raincoat");
} else {
print("Drive convertible");
}
}Code language: Dart (dart)
2. if-case Pattern Matching Branch (Dart 3.0+)
- Matches a pattern and destructures to bind variables accessible within the current scope upon successful match.
- Falls back to else when matching fails;
whenguards can be appended for extra boolean checks. - Designed for single-value single-pattern matching; use switch statements for multi-pattern matching scenarios.
void main() {
final pair = [100, 220];
// Matches a two-element list holding two integers
if (pair case [int x, int y]) {
print("Coordinate: $x,$y");
} else {
throw FormatException("Invalid coordinate");
}
}Code language: Dart (dart)
Execution Output
D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Coordinate: 10,20Code language: CSS (css)
2: if-case with when Guard
void main() {
final data = [8, 9];
if (data case [int a, int b] when a > 5 && b > 5) {
print("Both numbers are greater than 5");
} else {
print("Numbers do not meet criteria");
}
}Code language: Dart (dart)
Execution Output
D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Both numbers are greater than 5Code language: CSS (css)
3. switch Statements
Traditional procedural switch statement with no return value
- Cases support all pattern types: strings, constants, logical OR patterns, relational patterns
- Non-empty cases break automatically without manual break statements; empty cases trigger fallthrough by default
- Two methods for manual fallthrough:
continue labelor consecutive empty case blocks defaultblock executes when no case matches; compatible withwhenguards
1: Basic String Matching
void main() {
String command = "OPEN";
switch (command) {
case "CLOSED":
print("Execute close logic");
case "PENDING":
print("Execute pending logic");
case "OPEN":
print("Execute open logic");
default:
print("Unknown command");
}
}
Code language: Dart (dart)
Execution Output
D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Execute open logicCode language: CSS (css)
2: Empty Case Fallthrough + continue Label Jump
String command = "OPEN";
switch (command) {
case "OPEN":
print("Open operation"); // ① Matches OPEN and prints message
continue newCase; // ② Jumps straight to newCase marker, no backtracking
case "DENIED":
case "CLOSED":
print("Close operation");
newCase: // ③ Target jump label
case "PENDING":
print("Unified pending handling logic"); // ④ Runs directly without checking command == "PENDING"
}Code language: Dart (dart)
Execution Output
D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Open operation
Unified pending handling logicCode language: CSS (css)
In this example, does
continue newCase;jump to the newCase marker? But the subsequent case “PENDING” won’t match, so why doesprint("Unified pending handling logic");still run?Many readers will have this exact question
continue label; does not re-evaluate the matched value. Instead, it directly jumps to the entry point of the case marked by the label, skips all conditional checks, and executes code inside that branch immediately.
3: switch + when Guards
void main() {
int score = 88;
switch (score) {
case int s when s >= 90:
print("Excellent");
case int s when s >= 60:
print("Pass");
default:
print("Fail");
}
}
Code language: Dart (dart)
Output: Pass
The code first verifies the input type must be integer. If type matches, assign value to variable s. After that, the when guard runs secondary validation. Code inside the case only executes when s satisfies the when condition. Note that even with two separate int s declarations, they exist within isolated scopes and do not conflict.
Each case int s creates an independent pattern binding with scope limited exclusively to its own case branch, with zero cross-branch interference.
You can use entirely different variable names for each case without altering functionality.
switch (score) {
case int a when a >= 90:
print("Excellent");
case int b when b >= 60:
print("Pass");
default:
print("Fail");
}Code language: Dart (dart)
4. switch Expressions
Syntax available starting Dart 3.0+, returns a value and frequently used for variable assignment
Differences from switch Statements
- No
casekeyword; patterns and return expressions separated via=> - Every branch must evaluate to an expression, branches separated by commas
- No implicit fallthrough; all branches self-contained. Use the underscore wildcard
_as fallback instead of default when no patterns match - Compatible with assignment, print calls, return statements and all other expression contexts
1: Basic Character Operator Matching (Logical OR Pattern)
void main() {
const slash = 47, star = 42, comma = 44;
int charCode = 42;
final token = switch (charCode) {
slash || star => "Operator",
comma => "Punctuation",
_ => "Invalid character"
};
print(token);
}
Code language: Dart (dart)
Execution Output
Operator
This sample defines constants storing ASCII values for three symbols. The charCode variable is checked against patterns to classify it as operator, punctuation or unrecognized symbol.
2: switch Expression + when Guard
void main() {
int age = 22;
String level = switch (age) {
int a when a < 18 => "Minor",
int a when a < 30 => "Young Adult",
_ => "Adult"
};
print(level);
}Code language: Dart (dart)
Execution Output
Young Adult
3: Direct switch Expression Inside Function Return
void main() {
print(getDesc(3));
}
String getDesc(int num) => switch (num) {
1 => "One",
2 => "Two",
_ => "Other number"
};
Code language: Dart (dart)
Execution Output
Other number
Note: This switch construct is an expression, and getDesc is an arrow function — a distinctive Dart syntax absent from other C-style languages.
5. Exhaustiveness Checking
Triggers compile-time error when input values lack matching branches, forcing developers to cover all possible cases. Sealed classes and enums natively support exhaustiveness checks without requiring _ or default fallbacks.
1: Non-exhaustive Nullable Boolean Error (Comment out code and run to view error)
void main() {
bool? nullableBool = null;
// Uncommenting below code causes compile error: missing null match branch with no _/default fallback
/*
switch (nullableBool) {
case true:
print("True");
case false:
print("False");
}
*/
// Fix: Add null case or wildcard _ fallback
switch (nullableBool) {
case true:
print("True");
case false:
print("False");
case null:
print("Null value");
}
}Code language: PHP (php)
Execution Output
Null valueCode language: PHP (php)
2: Sealed Classes
Algebraic data type enforcing exhaustive pattern matching
import 'dart:math' as math;
// Sealed class: Subclasses may only be defined within this source file
sealed class Shape {}
// Subclass Square with side length parameter
class Square implements Shape {
final double length;
Square(this.length);
}
// Subclass Circle with radius parameter
class Circle implements Shape {
final double radius;
Circle(this.radius);
}
// Arrow function using switch expression to calculate area based on Shape subclass
double calculateArea(Shape shape) => switch (shape) {
Square(length: var l) => l * l,
Circle(radius: var r) => math.pi * r * r
// Adding new Shape subclass triggers compile error alerting missing corresponding case
};
void main() {
final Shape circle = Circle(5);
print(calculateArea(circle));
final Shape square = Square(4);
print(calculateArea(square));
}
Code language: Dart (dart)
Execution Output
78.53981633974483
16.0Code language: CSS (css)
3: Exhaustive Matching with Enums
No need for underscore _ fallback wildcard
void main() {
final Animal animal = Animal.cat;
String name = switch (animal) {
Animal.dog => "Dog",
Animal.cat => "Cat"
};
print(name);
}
enum Animal { dog, cat }
Code language: Dart (dart)
Output: Cat
Enums contain a fixed finite set of values with no unexpected variants, eliminating requirement for fallback logic.
6. when Guards
Fully compatible with if-case, switch statements and switch expressions
Purpose of when: Runs additional boolean validation after successful pattern match. If guard evaluates false, execution proceeds to next branch without exiting the switch block.
1. if-case + when
void main() {
List<int> arr = [10, 20];
if (arr case [int x, int y] when x + y > 20) {
print("Sum of two numbers exceeds 20");
}
}Code language: Dart (dart)
Sum of two numbers exceeds 20
The code first validates the input matches a two-integer list pattern, then uses when to verify their combined value exceeds 20.
2. switch Statement + when
void main() {
int num = 15;
switch(num) {
case int n when n % 2 == 0:
print("Even number");
case int n when n % 2 != 0:
print("Odd number");
}
}
Code language: PHP (php)
Output: Odd number
Checks integer type first, then executes the when guard condition.
3. switch Expression + when
void main() {
int num = 15;
String res = switch(num) {
int n when n % 2 == 0 => "Even number",
_ => "Odd number"
};
print(res);
}
Code language: PHP (php)