Dart: Dart Operators
Last updated: 2026-08-26
Operators are the verbs of code — master them to make data "move."
1. What You Will Learn
- Arithmetic / Relational / Logical / Bitwise Operators
- Assignment and Compound Assignment Operators
- Cascade operator
..and?..(a powerful tool for method chaining) - Null Safety Operators:
?./??/??= - Type Test Operators: is / is! / as
2. A Developer's Real Story
(1) Pain Point: NullPointerException Crashing Batch Tasks
Bob's DataPipeline frequently crashed while processing millions of orders because some orders lacked optional fields. The code was littered with if (value != null) checks, but one was missed, causing the processing of 300,000 orders to halt and requiring a complete re-run.
(2) The Null Safety Operator Solution
Dart provides three operators — ?. (safe access), ?? (null coalescing), and ??= (null-aware assignment) — that make null handling concise and safe.
// Before: verbose null checking
String getCity(Order order) {
if (order.customer != null) {
if (order.customer!.address != null) {
return order.customer!.address!.city;
}
}
return 'Unknown';
}
// After: null-safe operators
String getCity(Order order) =>
order.customer?.address?.city ?? 'Unknown';
> **Output:** Run in local DartPad or `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x; results may vary slightly with SDK version.
(3) Benefits
- 60% reduction in lines of code, making null-handling logic clear at a glance.
- Cascade operator
..enables smoother method chaining. - Type test operators make type checking safer.
3. Arithmetic and Relational Operators
(1) Arithmetic Operators
▶ Example
> **Output:** Run in local DartPad or `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x; results may vary slightly with SDK version.
: Basic Arithmetic
void main() {
int a = 17;
int b = 5;
print(a + b); // 22 - Addition
print(a - b); // 12 - Subtraction
print(a * b); // 85 - Multiplication
print(a / b); // 3.4 - Division (returns double)
print(a ~/ b); // 3 - Integer division
print(a % b); // 2 - Modulo (remainder)
// Prefix and postfix increment
int count = 10;
print(count++); // 10 (returns then increments)
print(count); // 11
print(++count); // 12 (increments then returns)
}
> **Output:** Run in local DartPad or `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x; results may vary slightly with SDK version.
| Operator | Meaning | Example | Result |
|---|---|---|---|
+ |
Addition | 17 + 5 |
22 |
- |
Subtraction | 17 - 5 |
12 |
* |
Multiplication | 17 * 5 |
85 |
/ |
Division (returns double) | 17 / 5 |
3.4 |
~/ |
Integer Division | 17 ~/ 5 |
3 |
% |
Modulo (Remainder) | 17 % 5 |
2 |
(2) Relational Operators
▶ Example
> **Output:** Run in local DartPad or `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x; results may vary slightly with SDK version.
: Comparison Operations
void main() {
int orders = 1500;
int threshold = 1000;
print(orders > threshold); // true
print(orders < threshold); // false
print(orders >= 1500); // true
print(orders <= 1500); // true
print(orders == 1500); // true
print(orders != 1000); // true
// String comparison is lexicographic
print('apple' < 'banana'); // true
print('USD' == 'USD'); // true
}
> **Output:** Run in local DartPad or `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x; results may vary slightly with SDK version.
4. Logical and Bitwise Operators
(1) Logical Operators
▶ Example
> **Output:** Run in local DartPad or `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x; results may vary slightly with SDK version.
: Logical Operations
void main() {
bool isProduction = true;
bool hasErrors = false;
print(isProduction && !hasErrors); // true - AND + NOT
print(isProduction || hasErrors); // true - OR
print(!isProduction); // false - NOT
// Short-circuit evaluation
String? name = null;
// name.length > 0 would crash, but:
print(name != null && name.isNotEmpty); // false (safe)
}
> **Output:** Run in local DartPad or `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x; results may vary slightly with SDK version.
| Operator | Meaning | Short-Circuit |
|---|---|---|
&& |
Logical AND | Left false -> right not evaluated |
| ` | ` | |
! |
Logical NOT | — |
(2) Bitwise Operators
▶ Example
> **Output:** Run in local DartPad or `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x; results may vary slightly with SDK version.
: Bitwise Operations
void main() {
int flags = 0b1010; // 10 in binary
int mask = 0b1100; // 12 in binary
print(flags & mask); // 8 (0b1000) - AND
print(flags | mask); // 14 (0b1110) - OR
print(flags ^ mask); // 6 (0b0110) - XOR
print(~flags); // -11 (inverted bits) - NOT
print(flags << 2); // 40 (0b101000) - Left shift
print(flags >> 1); // 5 (0b0101) - Right shift
}
> **Output:** Run in local DartPad or `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x; results may vary slightly with SDK version.
5. Assignment and Compound Assignment Operators
▶ Example
> **Output:** Run in local DartPad or `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x; results may vary slightly with SDK version.
: Compound Assignment
void main() {
int total = 0;
double revenue = 0.0;
total += 100; // total = total + 100 = 100
total -= 30; // total = 70
total *= 2; // total = 140
total ~/= 3; // total = 46 (integer division)
revenue += 52500.75; // revenue = 52500.75
// Null-aware assignment
String? outputPath;
outputPath ??= '/tmp/output'; // Assign if null
print(outputPath); // /tmp/output
outputPath ??= '/other/path'; // Not assigned (already non-null)
print(outputPath); // /tmp/output
}
> **Output:** Run in local DartPad or `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x; results may vary slightly with SDK version.
| Operator | Equivalent To | Example |
|---|---|---|
+= |
a = a + b |
total += 100 |
-= |
a = a - b |
total -= 30 |
*= |
a = a * b |
total *= 2 |
~/= |
a = a ~/ b |
total ~/= 3 |
??= |
a = a ?? b |
path ??= '/default' |
6. Cascade Operator .. and ?..
(1) Cascade Operator
The cascade operator .. allows performing multiple operations on the same object without repeatedly referencing the variable name.
graph TD A[Operator System] --> B[Arithmetic + Relational + Logical] A --> C[Cascade ..] A --> D[Null Safety ?. ?? ??=] A --> E[Type Test is/as] C --> C1["object..method1()..method2()"]
> **Output:** Run in local DartPad or `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x; results may vary slightly with SDK version.
▶ Example
> **Output:** Run in local DartPad or `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x; results may vary slightly with SDK version.
: Cascade Operator
class DataPipeline {
String name = '';
int batchSize = 0;
bool verbose = false;
final List<String> sources = [];
void start() => print('$name started');
}
void main() {
// Without cascade - repetitive
final pipeline1 = DataPipeline();
pipeline1.name = 'Analytics';
pipeline1.batchSize = 50000;
pipeline1.verbose = true;
pipeline1.sources.add('orders.csv');
pipeline1.start();
// With cascade - fluent and clean
final pipeline2 = DataPipeline()
..name = 'Analytics'
..batchSize = 50000
..verbose = true
..sources.add('orders.csv')
..start();
}
> **Output:** Run in local DartPad or `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x; results may vary slightly with SDK version.
▶ Example
> **Output:** Run in local DartPad or `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x; results may vary slightly with SDK version.
: Null-Safe Cascade ?..
class Customer {
String? name;
String? email;
}
void main() {
Customer? customer;
// Null-safe cascade - skipped if object is null
customer?..name = 'Bob'..email = 'bob@example.com';
print(customer); // null (cascade was skipped)
customer = Customer();
customer?..name = 'Bob'..email = 'bob@example.com';
print(customer?.name); // Bob
}
> **Output:** Run in local DartPad or `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x; results may vary slightly with SDK version.
| Operator | Meaning | When Object is null |
|---|---|---|
.. |
Cascade operation | Error |
?.. |
Null-safe cascade | Skips entire cascade |
7. Null Safety Operators
▶ Example
> **Output:** Run in local DartPad or `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x; results may vary slightly with SDK version.
: Three Null Safety Operators
class Address {
final String city;
Address(this.city);
}
class Customer {
final String name;
final Address? address;
Customer(this.name, {this.address});
}
class Order {
final String id;
final Customer? customer;
Order(this.id, {this.customer});
}
void main() {
// ?. - null-safe access
Order order = Order('ORD-001');
print(order.customer?.name); // null (safe)
// print(order.customer.name); // Runtime error!
// ?? - null coalescing
String city = order.customer?.address?.city ?? 'Unknown';
print(city); // Unknown
// ??= - null-aware assignment
String? outputPath;
outputPath ??= '/tmp/reports';
print(outputPath); // /tmp/reports
outputPath ??= '/other/path';
print(outputPath); // /tmp/reports (unchanged)
}
> **Output:** Run in local DartPad or `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x; results may vary slightly with SDK version.
| Operator | Meaning | Example | Result |
|---|---|---|---|
?. |
Safe access | obj?.method() |
null or call result |
?? |
Null coalescing | value ?? default |
value if non-null, else default |
??= |
Null-aware assignment | var ??= value |
Assigns if null |
8. Type Test Operators
▶ Example
> **Output:** Run in local DartPad or `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x; results may vary slightly with SDK version.
: is / is! / as
void main() {
Object value = '1,500.00 USD';
// is - type check (returns bool)
if (value is String) {
print('String length: ${value.length}'); // Type promoted!
}
// is! - negative type check
if (value is! int) {
print('Not an integer');
}
// as - type cast (throws if wrong)
String text = value as String; // OK
// int number = value as int; // Runtime error!
// Safe cast pattern
if (value is String) {
String safe = value; // No cast needed (promoted)
print(safe.toUpperCase());
}
}
> **Output:** Run in local DartPad or `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x; results may vary slightly with SDK version.
| Operator | Meaning | On Failure |
|---|---|---|
is |
Type check | Returns false |
is! |
Negated type check | Returns true |
as |
Type cast | Throws TypeError |
9. Full Example: DataPipeline Order Processing with Operators
// ============================================
// DataPipeline Order Processing with Operators
// Demonstrates all operator categories
// ============================================
class Order {
final String id;
final double amount;
String? discountCode;
double? discountPercent;
Order({required this.id, required this.amount});
// Null-safe operators for discount calculation
double get discountedAmount =>
amount - (amount * (discountPercent ?? 0));
double get tax => discountedAmount * 0.08;
double get total => discountedAmount + tax;
String formatUSD() => '\$${total.toStringAsFixed(2)} USD';
}
class Report {
final List<Order> orders = [];
var totalRevenue = 0.0;
var orderCount = 0;
void addOrder(Order order) {
orders.add(order);
totalRevenue += order.total; // Compound assignment
orderCount++;
}
double get averageOrderValue =>
orderCount > 0 ? totalRevenue / orderCount : 0;
bool get isHighVolume => orderCount >= 1000;
String get statusLabel => isHighVolume ? 'High Volume' : 'Normal';
}
void main() {
final report = Report()
..addOrder(Order(id: 'ORD-001', amount: 1500.0)
..discountPercent = 0.1)
..addOrder(Order(id: 'ORD-002', amount: 3250.50)
..discountCode = 'SAVE20'
..discountPercent = 0.2)
..addOrder(Order(id: 'ORD-003', amount: 890.25));
print('=== DataPipeline Order Report ===');
for (final order in report.orders) {
final discount = order.discountPercent != null
? '${(order.discountPercent! * 100).toInt()}%'
: 'None';
print(' ${order.id}: ${order.formatUSD()} (Discount: $discount)');
}
print('\n--- Summary ---');
print('Orders: ${report.orderCount}');
print('Revenue: \$${report.totalRevenue.toStringAsFixed(2)} USD');
print('Average: \$${report.averageOrderValue.toStringAsFixed(2)} USD');
print('Status: ${report.statusLabel}');
// Type test
print('\nType check: report is ${report.runtimeType}');
print('Is Report: ${report is Report}');
}
> **Output:** Run in local DartPad or `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x; results may vary slightly with SDK version.
Output:
=== DataPipeline Order Report ===
ORD-001: $1458.00 USD (Discount: 10%)
ORD-002: $2807.43 USD (Discount: 20%)
ORD-003: $961.47 USD (Discount: None)
--- Summary ---
Orders: 3
Revenue: $5226.90 USD
Average: $1742.30 USD
Status: Normal
Type check: report is Report
Is Report: true
❓ FAQ
Q: What's the difference between
/and~/? A:/always returns a double (e.g., 17 / 5 = 3.4), while~/returns the integer division result (e.g., 17 ~/ 5 = 3). Use~/when you need an integer result.
Q: What is the return value of the cascade operator
..? A:..returns the object itself (not the return value of the last method), allowing continued chaining. This is a key difference from a regular method call.
Q: What's the difference between
??=and??? A:??is the null coalescing operator, which returns the non-null value.??=is the null-aware assignment operator, which only assigns if the variable is null and returns the result.x ??= yis equivalent tox = x ?? y.
Q: When should I use
asinstead ofis? A: Preferisfor type checking, as the compiler performs automatic type promotion. Only useaswhen you are sure of the type or need an explicit cast, as an incorrect cast will throw an exception.
Q: When to use
?..vs..? A: Use?..when the object might be null, and..when you are sure it is non-null.?..skips the entire cascade if the object is null.
Q: Does
==compare references or values? A: Dart's==defaults to reference comparison (same as Java), but many built-in types (String, int, double) override==to compare values. Custom classes need to override both==andhashCode.
Q: Are bitwise operators commonly used in practice? A: Not frequently, but they are essential in scenarios like permission systems (flags), network protocol parsing, and encryption algorithms. Daily development mainly uses arithmetic and logical operators.
📖 Summary
- Note the difference between arithmetic
/(returns double) and~/(integer division). - Logical operators
&&and||have short-circuit properties, useful for safe access. - Cascade operator
..simplifies method chaining;?..is its null-safe version. - The Null Safety trio:
?.(safe access),??(null coalescing),??=(null-aware assignment). - Type tests:
ischecks and promotes types;asforces a cast (throws exception if unsafe).
📝 Exercises
- Basic (Difficulty ⭐): Write code using arithmetic operators to calculate the average amount per order for 1,500,000 orders with a total of 75,000,000 USD. Use the correct division operator.
- Intermediate (Difficulty ⭐⭐): Use the cascade operator
..to create and configure an object, setting at least 4 properties and calling 2 methods. Then compare it to the version without using cascade. - Challenge (Difficulty ⭐⭐⭐): Implement a
SafeValue<T>class using the?.,??, and??=operators to provide safe value access, default values, and lazy assignment, ensuring no null pointer exceptions are thrown during any operation.