Skip to content

Operators

Understand Dart's comprehensive set of operators and how to use them effectively.


What is it?

Operators are symbols that perform operations on one or more operands (values). Dart provides a rich set of operators for arithmetic, comparison, logical operations, bitwise operations, and more. Many operators can be overridden for custom types.


Why does it exist?

Operators exist to:

  • Perform mathematical calculations
  • Compare values and make decisions
  • Manipulate data at a fundamental level
  • Provide concise syntax for common operations
  • Enable expressive and readable code
  • Work with both primitive types and custom objects

Arithmetic Operators

Basic Arithmetic

int a = 10;
int b = 3;

// Addition
int sum = a + b;           // 13

// Subtraction
int difference = a - b;    // 7

// Multiplication
int product = a * b;       // 30

// Division (double result)
double quotient = a / b;   // 3.3333333333333335

// Integer division (truncates)
int integerDivision = a ~/ b; // 3

// Modulo (remainder)
int remainder = a % b;     // 1

// Negation
int negative = -a;         // -10

Increment and Decrement

int count = 0;

// Postfix increment (uses then increments)
print(count++); // 0
print(count);   // 1

// Prefix increment (increments then uses)
print(++count); // 2
print(count);   // 2

// Postfix decrement
print(count--); // 2
print(count);   // 1

// Prefix decrement
print(--count); // 0
print(count);   // 0

Comparison Operators

Equality and Relational

int a = 5;
int b = 10;

// Equality
bool isEqual = a == b;        // false
bool isNotEqual = a != b;     // true

// Greater/Less than
bool isGreater = a > b;       // false
bool isLess = a < b;          // true
bool isGreaterOrEqual = a >= b; // false
bool isLessOrEqual = a <= b;   // true

// Identity (same object)
var obj1 = Object();
var obj2 = obj1;
bool sameObject = obj1 == obj2; // true (same instance)

Logical Operators

Boolean Logic

bool a = true;
bool b = false;

// Logical AND (both true)
bool andResult = a && b;     // false

// Logical OR (at least one true)
bool orResult = a || b;      // true

// Logical NOT (negation)
bool notResult = !a;         // false
bool notResult2 = !b;        // true

// Compound conditions
bool complex = (a && b) || !a; // false

Short-circuit Evaluation

// AND short-circuit (stops at first false)
bool result = false && expensiveOperation(); // expensiveOperation not called

// OR short-circuit (stops at first true)
bool result2 = true || expensiveOperation(); // expensiveOperation not called

Assignment Operators

Basic Assignment

// Simple assignment
var x = 5;

// Compound assignment
int y = 10;
y += 5;  // y = y + 5  (15)
y -= 3;  // y = y - 3  (12)
y *= 2;  // y = y * 2  (24)
y ~/= 3; // y = y ~/ 3 (8)
y %= 3;  // y = y % 3  (2)
y++;     // y = y + 1  (3)

// Null-aware assignment
String? name;
name ??= 'Guest'; // Assign if null
print(name); // 'Guest'

Null-aware Operators

Safe Access and Defaults

String? maybeName = null;

// Null-aware access (safe navigation)
int? length = maybeName?.length; // null (no error)

// Null coalescing (default value)
String displayName = maybeName ?? 'Guest'; // 'Guest'

// Null coalescing assignment
maybeName ??= 'Alice'; // Assigns 'Alice'

// Null-aware cascade
class Person {
  String? name;
  int? age;
}

Person? person = Person()
  ?..name = 'Alice'
  ..age = 25;

// Equivalent to:
// if (person != null) {
//   person.name = 'Alice';
//   person.age = 25;
// }

Type Operators

Type Checking and Casting

dynamic value = 'Hello';

// Type check (is)
bool isString = value is String;  // true
bool isInt = value is int;        // false

// Type check negation (is!)
bool notString = value is! String; // false

// Type cast (as)
String str = value as String;     // Safe cast

// Type cast with check
if (value is String) {
  // value is now treated as String
  print(value.length);
}

Bitwise Operators

Bit Manipulation

int a = 0b1010;  // 10 in decimal
int b = 0b1100;  // 12 in decimal

// Bitwise AND
int and = a & b;     // 0b1000 (8)

// Bitwise OR
int or = a | b;      // 0b1110 (14)

// Bitwise XOR (exclusive OR)
int xor = a ^ b;     // 0b0110 (6)

// Bitwise NOT (complement)
int not = ~a;        // ...11110101 (-11)

// Left shift (multiply by 2^)
int leftShift = a << 2; // 0b101000 (40)

// Right shift (divide by 2^)
int rightShift = a >> 1; // 0b0101 (5)

Cascade Notation

Chaining Operations

class Person {
  String name = '';
  int age = 0;

  void sayHello() {
    print('Hello, I am $name');
  }
}

// Without cascade
var person = Person();
person.name = 'Alice';
person.age = 25;
person.sayHello();

// With cascade (..)
var person2 = Person()
  ..name = 'Alice'
  ..age = 25
  ..sayHello();

// Null-aware cascade (?..)
Person? maybePerson;
maybePerson
  ?..name = 'Alice'
  ..age = 25
  ..sayHello();

Spread Operators

Expanding Collections

// Spread operator (...)
var list1 = [1, 2, 3];
var list2 = [0, ...list1, 4, 5];
// Result: [0, 1, 2, 3, 4, 5]

// Null-aware spread (...?)
List<int>? maybeList;
var list3 = [1, 2, ...?maybeList, 3];
// If maybeList is null, expands to nothing

// Spread with collections
var map1 = {'a': 1, 'b': 2};
var map2 = {'c': 3, ...map1};
// Result: {'c': 3, 'a': 1, 'b': 2}

Collection Operators

if and for in Collections

// Collection if
bool includeItem = true;
var items = [
  'item1',
  'item2',
  if (includeItem) 'item3', // Included only if true
];

// Collection for
var numbers = [1, 2, 3];
var doubled = [
  for (var n in numbers) n * 2, // [2, 4, 6]
];

// Combined
var list = [
  'a',
  if (condition) 'b',
  for (var i in [1, 2, 3]) i,
];

Operator Precedence

Precedence Table

// Highest precedence (evaluated first)
// ()  .  ?.  []  !  ~  - (unary)
// *  /  ~/  %
// +  -
// <<  >>
// &  ^  |
// <  <=  >  >=  as  is  is!
// ==  !=
// &&
// ||
// ??
// ?..  ..  ..
// =  ??=  +=  -=  etc.
// Lowest precedence (evaluated last)

// Example
int result = 2 + 3 * 4;     // 14 (multiplication first)
int result2 = (2 + 3) * 4;  // 20 (parentheses override)

Overriding Operators

Custom Operator Implementation

class Vector {
  final double x, y;

  Vector(this.x, this.y);

  // Override + operator
  Vector operator +(Vector other) {
    return Vector(x + other.x, y + other.y);
  }

  // Override - operator
  Vector operator -(Vector other) {
    return Vector(x - other.x, y - other.y);
  }

  // Override * operator (scalar multiplication)
  Vector operator *(double scalar) {
    return Vector(x * scalar, y * scalar);
  }

  // Override == operator
  @override
  bool operator ==(Object other) {
    if (identical(this, other)) return true;
    if (other is! Vector) return false;
    return x == other.x && y == other.y;
  }

  @override
  int get hashCode => x.hashCode ^ y.hashCode;
}

// Usage
var v1 = Vector(1, 2);
var v2 = Vector(3, 4);
var v3 = v1 + v2;      // Vector(4, 6)
var v4 = v1 * 2;       // Vector(2, 4)

Overridable Operators

// All operators that can be overridden
class Point {
  final int x, y;

  Point(this.x, this.y);

  // Unary operators
  Point operator -() => Point(-x, -y);

  // Binary operators
  Point operator +(Point other) => Point(x + other.x, y + other.y);
  Point operator -(Point other) => Point(x - other.x, y - other.y);
  Point operator *(int scalar) => Point(x * scalar, y * scalar);
  Point operator /(double scalar) => Point(x / scalar, y / scalar);
  Point operator ~/(int scalar) => Point(x ~/ scalar, y ~/ scalar);
  Point operator %(int scalar) => Point(x % scalar, y % scalar);

  // Comparison operators
  bool operator ==(Object other) => 
    other is Point && x == other.x && y == other.y;

  int operator <(Point other) => x < other.x && y < other.y;

  // Index operators
  int operator [](int index) {
    if (index == 0) return x;
    if (index == 1) return y;
    throw RangeError('Index out of range');
  }

  void operator []=(int index, int value) {
    if (index == 0) x = value;
    else if (index == 1) y = value;
    else throw RangeError('Index out of range');
  }
}

Best Practices

Use Operators for Readability

// Good: Concise and readable
int result = a + b * c;

// Bad: Overly complex one-liners
int result = (a + b) * (c - d) / (e + f) % 2;

// Better: Break complex operations
int sum = a + b;
int difference = c - d;
int product = sum * difference;
int result = product ~/ (e + f);

Null Safety with Operators

// Good: Safe null handling
String? name = getUserName();
String display = name ?? 'Guest';
int? length = name?.length;

// Bad: Unsafe null handling
String display = name!; // Danger!
int length = name.length; // Danger!

Operator Overloading Guidelines

// Good: Intuitive operator overloading
class Money {
  final double amount;
  Money(this.amount);

  Money operator +(Money other) => Money(amount + other.amount);
  Money operator -(Money other) => Money(amount - other.amount);
  bool operator <(Money other) => amount < other.amount;
}

// Bad: Counter-intuitive overloading
class Money {
  final double amount;
  Money(this.amount);

  Money operator +(Money other) => Money(amount * other.amount); // Confusing!
}

Common Mistakes

Assignment vs Comparison

Wrong:

if (x = 5) { // Assignment instead of comparison
  // ...
}

Correct:

if (x == 5) { // Comparison
  // ...
}

Short-circuit Gotchas

Wrong:

bool result = condition && functionWithSideEffects();
// functionWithSideEffects might not be called

Correct:

if (condition) {
  functionWithSideEffects();
}

Increment/Decrement Confusion

Wrong:

var x = 5;
var y = x++; // y = 5, x = 6
var z = ++x; // z = 7, x = 7
// Hard to track values

Correct:

var x = 5;
var y = x;
x = x + 1; // Clear and explicit

Summary

Dart's operators provide powerful and concise ways to manipulate data. Understanding operator precedence, null-aware operators, and proper usage patterns helps you write cleaner, safer, and more efficient code.


Next Steps

Now that you understand operators, continue to:


Did You Know?

  • You can override most operators in Dart
  • The null-aware operators (?. and ??) are unique to Dart
  • Dart uses short-circuit evaluation for && and ||
  • The cascade operator (..) is unique to Dart
  • Operator precedence follows standard mathematical rules
  • Some operators cannot be overridden (like && and ||)