Dart Expressions
last modified June 4, 2025
Expressions form the core of Dart programs, enabling computations that yield values. This tutorial explores Dart expressions, detailing operator types, evaluation rules, and unique features like cascade notation.
Dart Expressions Overview
In Dart, an expression is a code construct that evaluates to a value, such as literals, variables, operators, or function calls. Dart offers a diverse set of operators and expression types, facilitating concise and expressive code for various programming tasks.
Expression Type | Description | Etitle="Dart Code Example |
---|---|---|
Literal | Direct value representation | 5, 'hello', true |
Arithmetic | Mathematical operations | a + b, x * y |
Relational | Comparison operations | a > b, x == y |
Logical | Boolean operations | a && b, !flag |
Conditional | Ternary operator | a ? b : c |
Cascade | Sequential operations | obj..a()..b() |
Assignment | Value assignment | a = 5, b += 2 |
Literal Expressions
Literal expressions directly represent fixed values in code, supporting various data types in Dart, including integers (e.g., 42), doubles (e.g., 3.14), strings (using single, double, or triple quotes), booleans (true, false), lists, maps, and the null value. These literals provide a straightforward way to embed constant values in programs.
void main() { // Numeric literals int integer = 42; double floating = 3.14159; // String literals String singles = 'Single quotes'; String doubles = "Double quotes"; String multiline = ''' Multi-line string '''; // Boolean literals bool truthy = true; bool falsy = false; // List and Map literals List<int> numbers = [1, 2, 3]; Map<String, int> ages = {'Alice': 25, 'Bob': 30}; print(integer); print(floating); print(singles); print(doubles); print(multiline); print(truthy); print(falsy); print(numbers); print(ages); }
This example showcases various Dart literals, demonstrating how to define and print integers, doubles, strings (including multiline), booleans, lists, and maps, highlighting their direct use in code.
$ dart run literals.dart 42 3.14159 Single quotes Double quotes Multi-line string [1, 2, 3] {Alice: 25, Bob: 30}
Arithmetic Expressions
Dart supports standard arithmetic operations through operators like addition (+), subtraction (-), multiplication (*), division (/), integer division (~/), modulo (%), and unary negation (-). The division operator always returns a double, while increment (++) and decrement (--) operators modify values. These operators enable mathematical computations, including mixed-type operations.
void main() { int a = 10; int b = 3; double c = 5.0; // Basic operations print('Addition: ${a + b}'); print('Subtraction: ${a - b}'); print('Multiplication: ${a * b}'); print('Division: ${a / b}'); print('Integer division: ${a ~/ b}'); print('Modulo: ${a % b}'); print('Unary minus: ${-a}'); // Mixed type arithmetic print('Mixed addition: ${a + c}'); // Increment/decrement int counter = 0; print('Post-increment: ${counter++}'); print('After increment: $counter'); print('Pre-decrement: ${--counter}'); }
This example demonstrates arithmetic operations, including basic calculations, mixed-type addition, and increment/decrement operations, illustrating how Dart handles mathematical expressions.
$ dart run arithmetic.dart Addition: 13 Subtraction: 7 Multiplication: 30 Division: 3.3333333333333335 Integer division: 3 Modulo: 1 Unary minus: -10 Mixed addition: 15.0 Post-increment: 0 After increment: 1 Pre-decrement: 0
Relational and Logical Expressions
Relational operators, such as equal (==), not equal (!=), greater than (>), and less than (<), compare values to produce boolean results. Logical operators, including AND (&&), OR (||), and NOT (!), combine boolean expressions. Dart employs short-circuit evaluation, skipping unnecessary operations (e.g., in && if the first operand is false), enhancing efficiency and safety in logical expressions.
void main() { int a = 5; int b = 10; // Relational operators print('Equal: ${a == b}'); print('Not equal: ${a != b}'); print('Greater than: ${a > b}'); print('Less than: ${a < b}'); print('Greater or equal: ${a >= b}'); print('Less or equal: ${a <= b}'); // Logical operators bool x = true; bool y = false; print('AND: ${x && y}'); print('OR: ${x || y}'); print('NOT: ${!x}'); // Short-circuit evaluation String? name; bool isValid = name != null && name.isNotEmpty; print('Is valid: $isValid'); }
This example illustrates relational comparisons and logical operations, including a demonstration of short-circuit evaluation with a nullable string, showing how Dart ensures safe and efficient expression evaluation.
$ dart run relational_logical.dart Equal: false Not equal: true Greater than: false Less than: true Greater or equal: false Less or equal: true AND: false OR: true NOT: false Is valid: false
Conditional Expressions
Dart's conditional expressions provide concise decision-making constructs. The ternary operator (?:) acts as a compact if-else statement, while the null- coalescing operator (??) supplies default values for null variables. Null-aware operators, such as conditional property access (?.) and null-aware assignment (??=), ensure safe handling of nullable types, making code robust and succinct.
void main() { int age = 20; // Ternary operator String status = age >= 18 ? 'Adult' : 'Minor'; print('Status: $status'); // Null-coalescing operator String? name; String displayName = name ?? 'Guest'; print('Welcome, $displayName'); // Conditional property access List<int>? numbers; int? first = numbers?.first; print('First number: $first'); // Null-aware assignment name ??= 'Anonymous'; print('Name: $name'); }
This example showcases conditional expressions, including the ternary operator for age-based status, null-coalescing for default names, and null-aware operators for safe list access and assignment, demonstrating concise control flow.
$ dart run conditional.dart Status: Adult Welcome, Guest First number: null Name: Anonymous
Cascade Notation
Dart's cascade notation (..) enables multiple operations on the same object in a fluent manner, returning the original object after each operation. This feature is particularly useful for builder-style APIs and simplifies code by reducing repetitive object references, enhancing readability and maintainability in sequential method calls.
class Person { String name = ''; int age = 0; void greet() => print('Hello, $name!'); void birthday() => age++; } void main() { // Without cascade Person p1 = Person(); p1.name = 'Alice'; p1.age = 30; p1.greet(); // With cascade Person p2 = Person() ..name = 'Bob' ..age = 25 ..greet() ..birthday(); print('Bob\'s age: ${p2.age}'); // Nested cascades StringBuffer sb = StringBuffer() ..write('Hello') ..write(' ') ..writeAll(['Dart', '!'], ' '); print(sb.toString()); }
This example contrasts traditional and cascade notation for object operations, using a Person class and StringBuffer to show how cascades streamline multiple method calls and property assignments.
$ dart run cascade.dart Hello, Alice! Hello, Bob! Bob's age: 26 Hello Dart !
Assignment Expressions
Assignment expressions in Dart store values in variables using the basic assignment operator (=) or compound operators like +=, *=, and ~/=. These expressions evaluate to the assigned value, enabling chained assignments. Null-aware assignment (??=) assigns a value only if the variable is null, providing a safe way to set defaults.
void main() { // Simple assignment int x = 5; print('x = $x'); // Compound assignment x += 3; print('x += 3 → $x'); x ~/= 2; print('x ~/= 2 → $x'); // Assignment as expression int y; print('y = ${y = x * 2}'); // Null-aware assignment int? z; z ??= 10; print('z = $z'); }
This example demonstrates simple and compound operations, assignment as an expression, and null-aware assignment, illustrating how Dart manages variable updates and default values.
$ dart run assignment.dart x = 5 x += 3 → 8 x ~/= 2 → 4 y = 8 z = 10
Expression Evaluation Order
Dart evaluates expressions based on operator precedence and associativity. Operators like multiplication have higher precedence than addition, while parentheses override default precedence. Most operators are left-associative, processing from left to right, except for assignment and conditional operators, which are right-associative rules, ensuring predictable computation order.
void main() { // Operator precedence int result = 2 + 3 * 4; print('2 + 3 * 4 = $result'); // Parentheses change order result = (2 + 3) * 4; print('(2 + 3) * 4 = $result'); // Left-associative operators result = 10 - 4 - 2; print('10 - 4 - 2 = $result'); // Right-associative operators bool a = false, b = true, c = false; bool logical = a && b || c; print('a && b || c = $logical'); }
This example illustrates operator precedence, the effect of parentheses, and associativity in expression evaluation, showing how Dart processes complex expressions systematically.
$ dart run evaluation_order.dart 2 + 3 * 4 = 14 (2 + 3) * 4 = 20 10 - 4 - 2 = 4 a && b || c = false
Bitwise Expressions
Dart's bitwise operators manipulate integer bits, enabling low-level operations like setting flags or optimizing computations. Operators include AND (&), OR (|), XOR (^), NOT (~), left shift (<<), and right shift (>>). These are useful in scenarios like graphics processing or protocol implementations, where direct bit manipulation is required.
void main() { int a = 5; // Binary: 0101 int b = 3; // Binary: 0011 // Bitwise operators print('Bitwise AND: ${a & b}'); // 0101 & 0011 = 0001 print('Bitwise OR: ${a | b}'); // 0101 | 0011 = 0111 print('Bitwise XOR: ${a ^ b}'); // 0101 ^ 0011 = 0110 print('Bitwise NOT: ${~a}'); // ~0101 = ...1010 (inverts bits) print('Left shift: ${a << 1}'); // 0101 << 1 = 1010 print('Right shift: ${a >> 1}'); // 0101 >> 1 = 0010 // Using bitwise for flags int read = 1; // 0001 int write = 2; // 0010 int permissions = read | write; // 0011 print('Has read: ${(permissions & read) != 0}'); }
This example demonstrates bitwise operations on integers, showing AND, OR, XOR, NOT, and shift operations, along with a practical use case for managing permission flags, highlighting their utility in low-level programming.
$ dart run bitwise.dart Bitwise AND: 1 Bitwise OR: 7 Bitwise XOR: 6 Bitwise NOT: -6 Left shift: 10 Right shift: 2 Has read: true
Spread Operator Expressions
The spread operator (...) in Dart expands elements of a collection, enabling concise merging or copying of lists, sets, or maps. The null-aware spread operator (...?) safely handles null collections, preventing runtime errors. This feature simplifies collection manipulation in expressions, enhancing code readability and flexibility.
void main() { // Spread operator with lists List<int> part1 = [1, 2]; List<int> part2 = [3, 4]; List<int> combined = [...part1, ...part2, 5]; print('Combined list: $combined'); // Null-aware spread List<int>? optional; List<int> safeList = [...part1, ...?optional, 6]; print('Safe list: $safeList'); // Spread with maps Map<String, int> map1 = {'a': 1, 'b': 2}; Map<String, int> map2 = {'c': 3}; Map<String, int> merged = {...map1, ...map2, 'd': 4}; print('Merged map: $merged'); // Nested spread List<List<int>> nested = [ [1, 2], [3, 4] ]; List<int> flattened = [...nested[0], ...nested[1]]; print('Flattened: $flattened'); }
This example showcases the spread operator for combining lists and maps, the null-aware spread for safe handling of nullable collections, and flattening nested lists, demonstrating its power in collection expressions.
$ dart run spread.dart Combined list: [1, 2, 3, 4, 5] Safe list: [1, 2, 6] Merged map: {a: 1, b: 2, c: 3, d: 4} Flattened: [1, 2, 3, 4]
Switch Expressions
Dart's switch
expressions provide a concise way to select a value
based on multiple conditions. Unlike traditional switch
statements,
switch expressions can be used directly in assignments and return values, making
code more expressive and reducing boilerplate. Dart supports pattern matching
and null safety in switch expressions, allowing for powerful and readable
branching logic.
void main() { var grade = 'B'; var result = switch (grade) { 'A' => 'Excellent', 'B' => 'Good', 'C' => 'Average', 'D' => 'Below average', 'F' => 'Fail', _ => 'Unknown', }; print('Grade: $grade, Result: $result'); // Pattern matching with types Object value = 42; var type = switch (value) { int i => 'Integer', double d => 'Double', String s => 'String', _ => 'Other', }; print('Type: $type'); }
This example demonstrates Dart's switch expressions for value selection and type pattern matching. The first switch maps a grade to a description, while the second uses type patterns to determine the type of a value. Switch expressions improve code clarity and reduce the need for verbose if-else chains.
Source
Dart Language Operators
Dart Language Tour: Expressions
Mastering Dart expressions is essential for crafting efficient and expressive code. From literals to advanced features like cascade and spread operators, Dart offers versatile tools for diverse programming needs.
Author
List all Dart tutorials.