DART: Operators

Dart provides a comprehensive set of operators for arithmetic, comparison, logical operations, and null safety. Dart's null-aware operators are especially important in modern Dart code — they provide safe, concise ways to handle nullable values without verbose null checks.

Operators are the tools you use to compute values, compare data, and make decisions. Dart's operator set covers the familiar arithmetic and relational operators found in most languages, and adds a powerful set of null-aware operators that are central to Dart's sound null safety system.


Arithmetic Operators

Mathematical operators perform standard numeric computations.

dart
int a = 20;
int b = 7;

print(a + b);   // 27 — addition
print(a - b);   // 13 — subtraction
print(a * b);   // 140 — multiplication
print(a / b);   // 2.857... — division (returns double)
print(a ~/ b);  // 2 — integer division (truncates decimal)
print(a % b);   // 6 — modulo (remainder)

Assignment and Compound Assignment

dart
int num = 10;
num += 5;    // num = num + 5   --> 15
num -= 3;    // num = num - 3   --> 12
num *= 2;    // num = num * 2   --> 24
num ~/= 5;   // num = num ~/ 5  --> 4
num %= 3;    // num = num % 3   --> 1

Increment and Decrement

dart
int x = 5;
print(x++);  // 5 — post-increment (returns value, then increments)
print(x);    // 6

print(++x);  // 7 — pre-increment (increments, then returns value)
print(x);    // 7

print(x--);  // 7 — post-decrement
print(--x);  // 5 — pre-decrement


Relational (Comparison) Operators

Relational operators compare two values and return a bool.

dart
int num = 10;

print(num == 10);   // true  — equal to
print(num != 5);    // true  — not equal to
print(num > 5);     // true  — greater than
print(num < 20);    // true  — less than
print(num >= 10);   // true  — greater than or equal to
print(num <= 10);   // true  — less than or equal to

Ternary Operator

The ternary operator is a concise if-else expression.

dart
int x = 100;
var label = x % 2 == 0 ? 'Even' : 'Odd';
print(label);  // Even

// Equivalent to:
// if (x % 2 == 0) { label = 'Even'; } else { label = 'Odd'; }

Type Test Operator

The is operator checks whether a value is an instance of a given type.

dart
dynamic value = 42;

if (value is int) {
  print('Is an integer');         // Prints this
}
if (value is! String) {
  print('Is not a string');       // Prints this
}


Logical Operators

Logical operators combine boolean expressions.

dart
int age = 25;
bool isStudent = true;

// AND — both conditions must be true
if (age >= 18 && age < 30) {
  print('Young adult');
}

// OR — at least one condition must be true
if (age < 18 || isStudent) {
  print('Gets a discount');
}

// NOT — inverts a boolean
if (!isStudent) {
  print('Full price');
}

Short-Circuit Evaluation

Dart evaluates logical expressions lazily:

dart
// If the first condition of && is false, the second is never evaluated
if (user != null && user.isActive) {
  // ...
}

// If the first condition of || is true, the second is never evaluated
if (cachedValue != null || loadFromDisk()) {
  // ...
}


Bitwise Operators

Bitwise operators manipulate individual bits of integer values.

dart
int flags = 0x0F;          // 00001111

print(flags & 0x03);       // AND  --> 00000011 (3)
print(flags | 0xF0);       // OR   --> 11111111 (255)
print(flags ^ 0xFF);       // XOR  --> 11110000 (240)
print(~flags);             // NOT  --> flips all bits
print(flags << 2);         // left shift  --> 00111100 (60)
print(flags >> 2);         // right shift --> 00000011 (3)


Null-Aware Operators

Null-aware operators are one of Dart's most useful features. They provide concise, safe ways to handle null values.

Null-Check Access `?.`

The ?. operator accesses a property or calls a method only if the object is not null. If the object is null, the entire expression evaluates to null instead of throwing.

dart
String? name;

// Without null-aware operator (verbose)
int? length = name != null ? name.length : null;

// With null-aware operator (concise)
int? length = name?.length;

// Chain multiple null-aware accesses
String? city = user?.address?.city;

Null-Coalescing `??`

The ?? operator returns the left operand if it is not null, otherwise returns the right operand.

dart
String? username;
String displayName = username ?? 'Guest';  // 'Guest' because username is null

int count = null ?? 0;   // 0
int score = 95 ?? 0;     // 95 (not null, so left side wins)

Null-Coalescing Assignment `??=`

The ??= operator assigns a value to a variable only if the variable is currently null. If it already has a value, nothing happens.

dart
String? config;

config ??= 'default-config';  // Assigns because config is null
print(config);                // default-config

config ??= 'other-value';     // Does NOT assign because config already has a value
print(config);                // default-config (unchanged)

Null Assertion `!`

The ! postfix operator asserts that a value is not null. If it is null, a runtime exception is thrown. Use only when you are certain the value cannot be null.

dart
String? name = 'Amr';
String definitelyNotNull = name!;  // Safe here because we know the value

// Dangerous:
String? maybeNull;
// String value = maybeNull!;  // Throws: Null check operator used on a null value


Cascade Operator `..`

The cascade operator allows you to make a sequence of operations on the same object without repeating the variable name.

dart
// Without cascade
var buffer = StringBuffer();
buffer.write('Hello');
buffer.write(', ');
buffer.write('Dart!');

// With cascade (more concise)
var buffer = StringBuffer()
  ..write('Hello')
  ..write(', ')
  ..write('Dart!');

print(buffer.toString());  // Hello, Dart!


Spread Operator `...` and `...?`

The spread operator inserts all elements of a collection into another collection.

dart
List<int> first = [1, 2, 3];
List<int> second = [4, 5, 6];

List<int> combined = [...first, ...second];
print(combined);  // [1, 2, 3, 4, 5, 6]

// Null-aware spread (safe if the list might be null)
List<int>? maybeList;
List<int> safe = [...?maybeList, 7, 8];
print(safe);  // [7, 8]


Operator Summary

md
Arithmetic:   +  -  *  /  ~/  %
Assignment:   =  +=  -=  *=  ~/=  %=
Increment:    ++  --
Comparison:   ==  !=  >  <  >=  <=
Ternary:      condition ? ifTrue : ifFalse
Type test:    is  is!
Logical:      &&  ||  !
Bitwise:      &  |  ^  ~  <<  >>
Null-aware:   ?.  ??  ??=  !
Cascade:      ..  ?..
Spread:       ...  ...?

Mastering the null-aware operators in particular will make your Dart code significantly cleaner and more robust. They are used constantly in real Flutter applications wherever data might be absent.