On this page25 sections
- Prerequisites
- Connection With Previous Lectures
- Learning Objectives
- 1. What Is Operator Precedence?
- 2. Parentheses Change the Order
- 3. Associativity: Same Level, Left to Right
- 4. Assignment Runs Last — and Right to Left
- 5. Unary Operators Bind Tightest
- 6. Arithmetic, Then Comparison, Then Logic
- 7. && Before ||
- 8. Comparisons Cannot Be Chained
- 9. ?? and ? : Sit Near the Bottom
- 10. The Precedence Table
- 11. Parentheses for Clarity
- 12. Custom Operators: A Preview
- 13. A Real-World Example
- Code Examples
- Common Beginner Mistakes
- Best Practices
- Mini Challenge
- Practice Questions
- Practice Answers
- Quick Revision
- Key Takeaways
- What's Next?
Lecture 21 — Operator Precedence & Custom Operators
| Category | 4 — Operators |
| Lecture | 021 of 140 |
| Estimated video length | 10 min |
| Target | Dart 3.12.2 |
| Level | Beginner |
You now know dozens of operators. When several of them meet in one line, Dart needs a rule for which one goes first — and so do you, or
2 + 3 * 4will surprise you.
Contents
Prerequisites · Connection · Objectives · Lesson · Code Examples · Common Mistakes · Best Practices · Mini Challenge · Practice Questions · Answers · Takeaways
Every error message in this lecture is real. Each one was produced by running the code shown on Dart 3.12.2, so you will recognize it when you meet it yourself.
Prerequisites
Students should already understand, from Lectures 004–020:
- variables,
int,double,bool,String, and string interpolation int.tryParseand nullable types such asint?(Lectures 010 and 012)- the if-null operator
??(Lecture 014) - arithmetic and assignment operators, including
~/,%, and+=(Lecture 017) - comparison operators
==,!=,<,>,<=,>=(Lecture 018) - logical operators
&&,||,!(Lecture 019) ++,--, the conditional operator? :, and the cascade..(Lecture 020)
Connection With Previous Lectures
In Lectures 017–020 we mostly used one or two operators at a time. Real expressions mix them:
int total = price * quantity + shipping;
bool canBuy = balance - price >= 0 && !isBlocked;Which part does Dart calculate first? If it worked strictly left to right, 2 + 3 * 4 would be
20. It is actually 14. Until you know the rules, every long expression is a guess — and a
wrong guess gives a wrong answer with no error message. This lecture gives you the rules,
and shows the one tool that makes them easy to follow: parentheses.
Learning Objectives
By the end of this lecture you will be able to:
- Explain what operator precedence and associativity mean
- Predict the result of an expression that mixes several operators
- Read Dart's precedence table for every operator taught so far
- Use parentheses to change the order of evaluation
- Use parentheses to make the order obvious, even when they are not required
- Recognize the expressions Dart refuses to compile, and why
- Describe what a custom operator is and what one looks like in a class
- Avoid the most common precedence mistakes with
??,!, and? :
1. What Is Operator Precedence?
Precedence is the rule that decides which operator runs first when several appear in one expression. An operator with higher precedence grabs its operands first.
Analogy — think of the order you get dressed: socks before shoes, no matter which you mention first. Dart's operators have the same kind of fixed order.
*always "dresses" before+.
void main() {
print(2 + 3 * 4);
}143 * 4 runs first and gives 12; then 2 + 12 gives 14.
2. Parentheses Change the Order
Anything inside parentheses ( ) is calculated first, whatever operators it contains.
void main() {
print(2 + 3 * 4);
print((2 + 3) * 4);
}14
20The parentheses force 2 + 3 to become 5 before the multiplication. This is the same rule
you learned in school maths.
3. Associativity: Same Level, Left to Right
When two operators have the same precedence, associativity decides. Most Dart operators are left-associative: they run from left to right.
void main() {
print(20 - 5 - 3);
print(100 / 10 / 2);
print(10 ~/ 3 * 3);
}12
5.0
920 - 5 - 3 means (20 - 5) - 3, not 20 - (5 - 3). In the last line, ~/ and * share a
level, so 10 ~/ 3 gives 3 first, and 3 * 3 gives 9.
4. Assignment Runs Last — and Right to Left
Assignment operators have the lowest precedence, so the whole right-hand side is finished before anything is stored. They are also right-associative:
void main() {
int total = 10;
total *= 2 + 1;
print(total);
int x = 1;
int y = 2;
x = y = 7;
print('$x $y');
}30
7 7total *= 2 + 1 means total = total * (2 + 1), which is 30 — not total * 2 + 1.
x = y = 7 runs right to left: y gets 7 first, then x gets that same value.
5. Unary Operators Bind Tightest
Operators that work on a single value — -x, !x, ++x, x++ — sit near the top of the
table. Postfix operators (written after the value) come first of all. That includes .
for calling a method:
void main() {
print(-2.abs());
print((-2).abs());
}-2
2-2.abs() means -(2.abs()): Dart calls abs() on 2 first, then applies the minus. Wrap
the negative number in parentheses when you want its absolute value.
6. Arithmetic, Then Comparison, Then Logic
Arithmetic runs before comparison, and comparison runs before && and ||. That is why
this needs no parentheses at all:
void main() {
int balance = 500;
int price = 300;
bool isBlocked = false;
bool canBuy = balance - price >= 0 && !isBlocked;
print(canBuy);
}trueDart reads it as ((balance - price) >= 0) && (!isBlocked): subtract, compare, then combine.
7. && Before ||
&& has higher precedence than ||, just as * is higher than +:
void main() {
int visits = 12;
bool isMember = visits >= 10;
bool hasCoupon = true;
bool isWeekend = false;
print(isMember || hasCoupon && isWeekend);
print((isMember || hasCoupon) && isWeekend);
}true
falseHere isMember is true (12 visits is at least 10). The first line means
isMember || (hasCoupon && isWeekend). The parentheses in the second
line change the meaning — and the answer.
8. Comparisons Cannot Be Chained
In maths you might write 0 < x < 10. Comparison and equality operators in Dart are
non-associative: you cannot put two of them side by side.
void main() {
int a = 5;
print(a < 10 < 20);
}Error: A comparison expression can't be an operand of another comparison expression.
Try putting parentheses around one of the comparisons.
print(a < 10 < 20);
^
Error: The operator '<' isn't defined for the type 'bool'.
Try correcting the operator to an existing operator, or defining a '<' operator.
print(a < 10 < 20);
^The second error explains the first: a < 10 is already a bool, and a bool cannot be
compared with < 20.
To check a range, write two comparisons and join them with &&:
a > 0 && a < 10.
9. ?? and ? : Sit Near the Bottom
The if-null operator ?? and the conditional operator ? : have lower precedence than
arithmetic, comparison, and logic. So the arithmetic on their right is finished first:
void main() {
int? extra = int.tryParse('15');
int score = extra ?? 0 + 10;
print(score);
}15You might expect 25. But Dart reads extra ?? (0 + 10): since extra is 15, the + 10
never happens. To add 10 in every case, write (extra ?? 0) + 10.
The conditional operator is right-associative, so a chain of them is read from the right:
void main() {
int score = 72;
String grade = score >= 90
? 'A'
: score >= 70
? 'B'
: 'C';
print(grade);
}BThis means score >= 90 ? 'A' : (score >= 70 ? 'B' : 'C'). It works, but it gets hard to
read fast. Lecture 023 shows a clearer way to choose between many options.
10. The Precedence Table
Here is every operator from this category, from highest precedence to lowest:
| Level | Operators | Name | Associativity |
|---|---|---|---|
| 1 | x++ x-- () [] . ?. x! | postfix | — |
| 2 | -x !x ++x --x | prefix (unary) | — |
| 3 | * / ~/ % | multiplicative | left |
| 4 | + - | additive | left |
| 5 | < > <= >= is is! as | relational & type test | none |
| 6 | == != | equality | none |
| 7 | && | logical AND | left |
| 8 | || | logical OR | left |
| 9 | ?? | if-null | left |
| 10 | ? : | conditional | right |
| 11 | .. ?.. | cascade | left |
| 12 | = += -= *= /= ~/= %= ??= | assignment | right |
Dart also has bitwise and shift operators (&, |, ^, ~, <<, >>, >>>); they sit
between levels 4 and 5. This course does not need them, so we leave them aside.
Notice that the cascade .. is almost at the bottom, just above assignment. That is why, in
Lecture 020, a whole cascade chain finished running before its result was stored in a
variable.
You do not need to memorise all twelve rows. Remember three things: unary first, maths before comparisons, comparisons before logic — and use parentheses for everything else.
11. Parentheses for Clarity
Parentheses are also allowed when they change nothing. Use them whenever a reader might hesitate:
void main() {
int age = 16;
bool hasTicket = true;
bool withParent = false;
bool canEnter = age >= 18 && hasTicket || withParent;
bool clearer = (age >= 18 && hasTicket) || withParent;
print(canEnter);
print(clearer);
}false
falseBoth lines give the same answer, but the second one shows the intent at a glance. If an expression still feels crowded, split it into named variables — that is the clearest option of all.
12. Custom Operators: A Preview
Every operator so far worked on built-in types such as int and String. Dart also lets you
define what an operator means for your own types. This is called operator overloading.
Your own types are made with classes, which we only begin in Lecture 052. So treat this as a preview: read it, run it, and don't worry about every keyword yet.
class Money {
final int amount;
Money(this.amount);
Money operator +(Money other) {
return Money(amount + other.amount);
}
Money operator *(int times) {
return Money(amount * times);
}
}
void main() {
Money lunch = Money(450);
Money coffee = Money(150);
Money total = lunch + coffee * 2;
print(total.amount);
total += Money(100);
print(total.amount);
}750
850How it works
class Moneydescribes a new type that holds oneintcalledamount.Money operator +(Money other)says: "when+sits between twoMoneyvalues, add their amounts and give back a newMoney."operator *does the same forMoney * int.- The precedence stays the same:
coffee * 2still runs before+, giving450 + 300 = 750. - Once
+exists,+=works for free.
You can define operators such as +, -, *, /, ~/, %, <, >, <=, >=, ==,
and []. You cannot define &&, ||, !, ??, ? :, or = — their meaning is fixed.
We will build classes properly in Lectures 052–058, and redefine == for our own types
when we cover Method Overriding (Lecture 060).
13. A Real-World Example
void main() {
int price = 450;
int quantity = 3;
int? coupon = int.tryParse('100');
bool isMember = false;
int subtotal = price * quantity - (coupon ?? 0);
bool freeShipping = subtotal >= 1000 || isMember;
int shipping = freeShipping ? 0 : 150;
int total = subtotal + shipping;
print('Subtotal: $subtotal');
print('Free shipping: $freeShipping');
print('Total: $total');
}Subtotal: 1250
Free shipping: true
Total: 1250Every precedence rule is at work: * before -, parentheses around ??, arithmetic before
>=, comparison before ||, and the conditional operator picking a value before = stores
it. Splitting the work into four named steps keeps each line easy to check.
Code Examples
Example 1 — Multiplication Before Addition
void main() {
print(10 + 4 * 2);
print((10 + 4) * 2);
print(10 - 6 / 2);
}18
28
7.0Example 2 — Left to Right at the Same Level
void main() {
print(50 - 20 - 10);
print(50 - (20 - 10));
print(60 / 6 * 2);
}20
40
20.0Example 3 — Remainder Shares a Level with *
void main() {
print(17 % 5 * 2);
print(17 % (5 * 2));
print(-3 % 5);
}4
7
2Example 4 — Unary Minus and a Method Call
void main() {
print(-7.abs());
print((-7).abs());
print(-7.abs() + 10);
}-7
7
3Example 5 — Comparison Before Logic
void main() {
int temperature = 28;
bool isSunny = true;
bool goOutside = temperature > 20 && temperature < 35 && isSunny;
print(goOutside);
}trueExample 6 — Compound Assignment Waits for the Right Side
void main() {
int points = 5;
points += 3 * 2;
print(points);
points -= 4 - 1;
print(points);
}11
8Example 7 — Student Marks
void main() {
int math = 80;
int science = 75;
int english = 85;
double wrong = math + science + english / 3;
double average = (math + science + english) / 3;
print('Without parentheses: ${wrong.toStringAsFixed(2)}');
print('Average: ${average.toStringAsFixed(2)}');
}Without parentheses: 183.33
Average: 80.00How it works
- In the first expression,
/runs before+, so onlyenglishis divided by3. - The parentheses add all three marks first, then divide the total.
- Both lines compile — only the parentheses decide which answer is correct.
Example 8 — Shopping Total with an Optional Discount
void main() {
int price = 1200;
int? discount = int.tryParse('');
int shipping = 100;
int total = price - (discount ?? 0) + shipping;
print('Total: $total');
}Total: 1300How it works
int.tryParse('')returnsnull, because empty text is not a number.(discount ?? 0)becomes0; the parentheses keep??from swallowing the rest.- Then
1200 - 0 + 100runs left to right, giving1300.
Example 9 — Bank Balance
void main() {
int balance = 2000;
int withdrawal = 1700;
int minimum = 500;
bool isFrozen = false;
bool allowed = (balance - withdrawal >= minimum) && !isFrozen;
print('Withdrawal allowed: $allowed');
}Withdrawal allowed: falseHow it works
balance - withdrawalgives300first.300 >= 500isfalse.!isFrozenistrue, butfalse && trueisfalse.- The parentheses are not required — they just make the three steps visible.
Example 10 — Temperature Conversion
void main() {
double celsius = 30;
double fahrenheit = celsius * 9 / 5 + 32;
double hot = 104;
double backToCelsius = (hot - 32) * 5 / 9;
print('30 °C = $fahrenheit °F');
print('104 °F = $backToCelsius °C');
}30 °C = 86.0 °F
104 °F = 40.0 °CHow it works
- Celsius to Fahrenheit needs no parentheses:
*and/run left to right, then+ 32. - Fahrenheit to Celsius does:
32must be subtracted before multiplying. - Without them,
hot - 32 * 5 / 9would subtract only17.77…, giving a wrong answer.
Common Beginner Mistakes
1. Chaining comparisons like in maths
// print(age > 0 < 120); // ✗Error: A comparison expression can't be an operand of another comparison expression.Write age > 0 && age < 120.
2. Forgetting that ?? comes after arithmetic
void main() {
int? bonus = int.tryParse('200');
int salary = bonus ?? 0 + 1000; // reads as bonus ?? (0 + 1000)
print(salary);
}200No error — just a wrong answer. Write (bonus ?? 0) + 1000.
3. Putting ! in front of a comparison
// int count = 3;
// print(!count == 3); // ✗Error: A value of type 'int' can't be assigned to a variable of type 'bool'.
print(!count == 3);
^! binds tighter than ==, so Dart tries !count first — and count is not a bool.
Put the comparison in parentheses, or better, use !=:
void main() {
int count = 3;
print(!(count == 3));
print(count != 3);
}false
false4. Joining text before a conditional operator
// int score = 72;
// print('Result: ' + score >= 50 ? 'Pass' : 'Fail'); // ✗Error: A value of type 'int' can't be assigned to a variable of type 'String'.
print('Result: ' + score >= 50 ? 'Pass' : 'Fail');
^+ runs first, so Dart tries 'Result: ' + score. Put the whole conditional inside
interpolation:
void main() {
int score = 72;
print('Result: ${score >= 50 ? 'Pass' : 'Fail'}');
}Result: Pass5. Expecting -2.abs() to be 2
The method call runs before the minus, so the result is -2. Write (-2).abs().
6. Leaving out parentheses in an average
a + b + c / 3 divides only c. Write (a + b + c) / 3.
Best Practices
- Add parentheses whenever a reader might have to think about the order.
- Always wrap
??in parentheses when it is part of a larger calculation. - Split long expressions into named variables, one idea per line.
- Keep
++and--on their own lines instead of inside bigger expressions. - Avoid chains of more than one
? :— later lectures teach clearer tools. - Prefer
!=over!( ... == ... ). - Test a tricky expression with small numbers you can check by hand.
Mini Challenge
Sara buys 3 notebooks at 120 each and one pen at 40. She has a coupon for 50 off the notebooks only. Write a single expression that calculates her total, using parentheses so the order is obvious, and print it.
Your program should print exactly:
Total: 350Try it yourself before opening the solution.
Show solution
void main() {
int notebookPrice = 120;
int notebooks = 3;
int penPrice = 40;
int coupon = 50;
int total = (notebookPrice * notebooks - coupon) + penPrice;
print('Total: $total');
}Total: 350How it works
notebookPrice * notebooksruns first:360.- The coupon comes off the notebooks:
360 - 50 = 310. - The pen is added last:
310 + 40 = 350. The parentheses aren't required here — they show that the coupon belongs to the notebooks.
Practice Questions
Work through these in order — they run from beginner to application to challenge. Predict each answer before you run the code.
Beginner
- Predict, then print:
8 + 2 * 5and(8 + 2) * 5. - Predict, then print:
20 - 6 - 4and20 - (6 - 4). - Predict, then print:
17 ~/ 4 * 2and17 ~/ (4 * 2).
Application
- Store Zayan's marks
70,85, and90, then print their average as adouble. - Store
isMember = false,hasCoupon = true,isWeekend = false. Predict and printisMember || hasCoupon && isWeekend, then the same with parentheses aroundisMember || hasCoupon. - This program is meant to print
25, but prints15. Fix it.int? extra = int.tryParse('15'); int score = extra ?? 0 + 10; - Convert
212°F to Celsius with the formula(F − 32) × 5 ÷ 9and print the result. - Predict the values of
aandb, then check:int a = 4; int b = a++ + ++a;
Challenge
- Ali can join a trip if he is at least 18 and has an ID, or if a parent comes with
him. Store
age = 15,hasId = true,withParent = true. Write the condition with parentheses that make the grouping clear, and printCan join: …. - Copy the
Moneyclass from section 12 and add anoperator -that subtracts twoMoneyvalues. Use it to print the result ofMoney(1000) - Money(350) * 2.
Practice Answers
Question 1 — Solution
void main() {
print(8 + 2 * 5);
print((8 + 2) * 5);
}18
50Question 2 — Solution
void main() {
print(20 - 6 - 4);
print(20 - (6 - 4));
}10
18Question 3 — Solution
void main() {
print(17 ~/ 4 * 2);
print(17 ~/ (4 * 2));
}8
217 ~/ 4 is 4, then 4 * 2 is 8. With parentheses, 17 ~/ 8 is 2.
Question 4 — Solution
void main() {
int math = 70;
int science = 85;
int english = 90;
double average = (math + science + english) / 3;
print(average.toStringAsFixed(2));
}81.67Question 5 — Solution
void main() {
bool isMember = false;
bool hasCoupon = true;
bool isWeekend = false;
print(isMember || hasCoupon && isWeekend);
print((isMember || hasCoupon) && isWeekend);
}false
falseBoth are false, but for different reasons: the first is false || (true && false), the
second is (false || true) && false.
Question 6 — Solution
void main() {
int? extra = int.tryParse('15');
int score = (extra ?? 0) + 10;
print(score);
}25Question 7 — Solution
void main() {
double fahrenheit = 212;
double celsius = (fahrenheit - 32) * 5 / 9;
print(celsius);
}100.0Question 8 — Solution
void main() {
int a = 4;
int b = a++ + ++a;
print('a: $a, b: $b');
}a: 6, b: 10a++ gives 4 and makes a equal 5. Then ++a makes a equal 6 and gives 6.
So b is 4 + 6 = 10. Because ++ binds tighter than +, both increments happen before the
addition — and code this hard to follow is exactly why ++ belongs on its own line.
Question 9 — Solution
void main() {
int age = 15;
bool hasId = true;
bool withParent = true;
bool canJoin = (age >= 18 && hasId) || withParent;
print('Can join: $canJoin');
}Can join: trueQuestion 10 — Solution
class Money {
final int amount;
Money(this.amount);
Money operator +(Money other) {
return Money(amount + other.amount);
}
Money operator -(Money other) {
return Money(amount - other.amount);
}
Money operator *(int times) {
return Money(amount * times);
}
}
void main() {
Money result = Money(1000) - Money(350) * 2;
print(result.amount);
}300Your own * keeps the built-in precedence, so Money(350) * 2 runs first (700), and
1000 - 700 gives 300.
Quick Revision
- What does operator precedence decide?
- What does associativity decide?
- What is
2 + 3 * 4, and why? - Which runs first:
&&or||? - Why does
a < b < cnot compile? - How does Dart read
x ?? 0 + 5? - Why is
-2.abs()equal to-2? - Which operators have the lowest precedence of all?
- Does a custom
*operator keep the precedence of the built-in*?
Key Takeaways
- Precedence decides which operator runs first; associativity decides the order between operators on the same level.
- Postfix and unary operators bind tightest; then
* / ~/ %; then+ -; then comparisons; then&&,||,??,? :, cascade, and finally assignment. - Most operators are left-associative;
? :and assignment are right-associative. - Comparison and equality operators cannot be chained — join them with
&&. ??runs after arithmetic, so wrap it in parentheses inside a calculation.- Parentheses override every rule, and are welcome even when they change nothing.
- A class can define its own operators such as
+and*; they keep the normal precedence. &&,||,!,??,? :, and=cannot be redefined.
What's Next?
Lecture 022 — if Statements. You can now build any condition you like and know exactly how
Dart evaluates it. But so far a condition only produces true or false — the program still
runs every line, every time. With if, a condition finally decides which code runs. That
is where Category 5 — Control Flow begins.