Beginner

Operators in Dart

Learn arithmetic, comparison, logical, assignment, and null-aware operators in Dart with practical Docker-ready examples

Operators are the building blocks of expressions. Dart provides a rich set of operators familiar to anyone with a C-style language background, plus a handful of features that reflect its modern design: integer-specific division, sound null safety, and cascade notation for chained method calls.

Because Dart is statically and strongly typed with sound null safety, the compiler verifies operator usage at compile time. You cannot add a String to an int implicitly, and the analyzer will refuse to dereference a potentially-null value with .. This makes operators safer to use than in many dynamic languages, while still feeling concise thanks to type inference.

In this tutorial you will learn how Dart handles arithmetic (including the distinction between / and ~/), comparisons, logical operators with short-circuit evaluation, compound assignment, string concatenation and interpolation, null-aware operators (?., ??, ??=), and a few Dart-specific operators like cascades (..) and conditional expressions.

Arithmetic Operators

Dart supports the standard arithmetic operators, with one important distinction: / always returns a double, while ~/ performs integer (truncating) division.

Create a file named operators_arithmetic.dart:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
void main() {
  int a = 17;
  int b = 5;

  print('a + b = ${a + b}');   // Addition
  print('a - b = ${a - b}');   // Subtraction
  print('a * b = ${a * b}');   // Multiplication
  print('a / b = ${a / b}');   // Division (returns double)
  print('a ~/ b = ${a ~/ b}'); // Integer division
  print('a % b = ${a % b}');   // Modulo (remainder)

  // Unary operators
  int x = 10;
  print('-x = ${-x}'); // Negation

  // Increment and decrement
  int counter = 0;
  counter++;           // Post-increment
  ++counter;           // Pre-increment
  print('counter = $counter');
}

Notice how 17 / 5 produces 3.4 (a double), while 17 ~/ 5 produces 3 (an int). Dart’s static type system tracks this for you — the result type of / is always double, even when both operands are int.

Comparison and Logical Operators

Comparison operators return bool. Logical operators (&&, ||, !) short-circuit just like in C, Java, and JavaScript.

Create a file named operators_logic.dart:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void main() {
  int x = 10;
  int y = 20;

  // Comparison operators
  print('x == y: ${x == y}');
  print('x != y: ${x != y}');
  print('x < y:  ${x < y}');
  print('x > y:  ${x > y}');
  print('x <= y: ${x <= y}');
  print('x >= y: ${x >= y}');

  // Logical operators
  bool inRange = x > 0 && x < 100;
  bool isEdge = x == 0 || x == 100;
  bool negated = !inRange;

  print('inRange: $inRange');
  print('isEdge:  $isEdge');
  print('negated: $negated');

  // Type checking with 'is' and 'is!'
  Object value = 'Dart';
  print('value is String: ${value is String}');
  print('value is! int:   ${value is! int}');
}

The is and is! operators are Dart-specific type tests. Combined with smart promotion, if (value is String) { ... } lets the analyzer treat value as a String inside the block without an explicit cast.

Assignment, Strings, and Conditional Expressions

Dart provides the usual compound assignment operators (+=, -=, *=, etc.) and uses + for string concatenation — though interpolation is more idiomatic. The ternary ?: and the if-null ?? operator make many if statements unnecessary.

Create a file named operators_assign.dart:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void main() {
  // Compound assignment
  int total = 10;
  total += 5;   // total = total + 5
  total -= 2;   // total = total - 2
  total *= 3;   // total = total * 3
  total ~/= 4;  // Integer divide-and-assign
  print('total = $total');

  // String concatenation vs interpolation
  String first = 'Hello';
  String second = 'Dart';
  String concat = first + ', ' + second + '!';
  String interp = '$first, $second!';
  print(concat);
  print(interp);

  // String repetition with *
  String line = '-' * 20;
  print(line);

  // Ternary conditional expression
  int score = 75;
  String grade = score >= 60 ? 'pass' : 'fail';
  print('grade: $grade');
}

The String * int operator repeats the string, a small convenience absent from many C-style languages. Compound operators evaluate the left-hand expression only once, which matters when it has side effects.

Null-Aware and Cascade Operators

Dart’s sound null safety introduces a family of operators for working with nullable values without sprinkling if (x != null) checks everywhere. The cascade operator (..) lets you chain calls on the same object — handy for builder-style configuration.

Create a file named operators_null.dart:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class Buffer {
  final List<String> lines = [];

  void write(String s) => lines.add(s);
  void newline() => lines.add('\n');
  String render() => lines.join();
}

void main() {
  // Null-aware operators
  String? maybeName;
  String? givenName = 'Ada';

  // ?? — if-null: use the right side when left is null
  String displayA = maybeName ?? 'anonymous';
  String displayB = givenName ?? 'anonymous';
  print('displayA: $displayA');
  print('displayB: $displayB');

  // ?. — null-aware member access
  print('length of maybeName: ${maybeName?.length}'); // null
  print('length of givenName: ${givenName?.length}'); // 3

  // ??= — assign only if currently null
  String? title;
  title ??= 'Untitled';
  title ??= 'Ignored'; // Skipped: title is already set
  print('title: $title');

  // Cascade (..) — operate on the same object repeatedly
  var buffer = Buffer()
    ..write('first')
    ..newline()
    ..write('second');
  print(buffer.render());
}

The cascade returns the receiver rather than the result of each method, which is why we can chain ..write(...)..newline() without losing the Buffer reference. Combined with ?., ??, and ??=, these operators are some of the most distinctly “Dart” features you’ll use daily.

Running with Docker

1
2
3
4
5
6
7
8
# Pull the official image
docker pull dart:stable

# Run each example
docker run --rm -v $(pwd):/app -w /app dart:stable dart run operators_arithmetic.dart
docker run --rm -v $(pwd):/app -w /app dart:stable dart run operators_logic.dart
docker run --rm -v $(pwd):/app -w /app dart:stable dart run operators_assign.dart
docker run --rm -v $(pwd):/app -w /app dart:stable dart run operators_null.dart

Expected Output

Running operators_arithmetic.dart:

a + b = 22
a - b = 12
a * b = 85
a / b = 3.4
a ~/ b = 3
a % b = 2
-x = -10
counter = 2

Running operators_logic.dart:

x == y: false
x != y: true
x < y:  true
x > y:  false
x <= y: true
x >= y: false
inRange: true
isEdge:  false
negated: false
value is String: true
value is! int:   true

Running operators_assign.dart:

total = 9
Hello, Dart!
Hello, Dart!
--------------------
grade: pass

Running operators_null.dart:

displayA: anonymous
displayB: Ada
length of maybeName: null
length of givenName: 3
title: Untitled
first
second

Key Concepts

  • Two division operators: / always returns a double; ~/ truncates to int. Choose deliberately based on the type you need.
  • Short-circuit logical operators: && and || stop evaluating as soon as the result is determined — useful for guarding against null or expensive operations.
  • Type-test operators: is and is! enable type promotion, letting the analyzer narrow a variable’s type inside a conditional block.
  • String interpolation beats +: '$name has ${items.length} items' is idiomatic; reserve + for cases where interpolation is awkward.
  • Null-aware family: ?. (safe access), ?? (default value), and ??= (assign-if-null) eliminate most explicit null checks under sound null safety.
  • Cascades chain on one object: obj..a()..b()..c() evaluates to obj, not to the result of c() — ideal for configuration and builders.
  • Compound assignments evaluate once: list[i++] += 1 increments i a single time, which differs from rewriting it as list[i++] = list[i++] + 1.
  • Operators are methods: Most arithmetic and comparison operators can be overloaded on your own classes by defining methods like operator +(other).

Running Today

All examples can be run using Docker:

docker pull dart:stable
Last updated:

Comments

Loading comments...

Leave a Comment

2000 characters remaining