On this page29 sections
- Prerequisites
- Connection With Previous Lectures
- Learning Objectives
- 1. Operators That Work on One Value
- 2. The Increment Operator ++
- 3. The Decrement Operator --
- 4. Prefix and Postfix
- 5. The Value an Increment Gives Back
- 6. Tracing Prefix and Postfix Step by Step
- 7. Where ++ and -- Cannot Be Used
- 8. The Conditional Expression ? :
- 9. It Is an Expression, So It Fits Anywhere
- 10. Both Sides Should Have the Same Type
- 11. Conditional Expression or ???
- 12. Building Text with a StringBuffer
- 13. The Cascade Operator ..
- 14. A Cascade Gives Back the Object, Not the Result
- 15. Creating and Filling an Object in One Step
- 16. The Null-Aware Cascade ?..
- 17. 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 20 — Increment, Conditional & Cascade Operators
| Category | 4 — Operators |
| Lecture | 020 of 140 |
| Estimated video length | 17 min |
| Target | Dart 3.12.2 |
| Level | Beginner |
Adding one, choosing between two values, and calling several methods on the same object are things programs do constantly. Dart has a short operator for each:
++,? :, and...
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 001–019:
- variables with
var,final, and explicit types int,double,String, andbool, and interpolation with$and${...}- nullable types (
int?) and the null-aware operators?.and?? - arithmetic and compound assignment such as
+=and-= - comparison operators (
==,>=,<) and logical operators (&&,||,!)
Connection With Previous Lectures
Look at three lines a real program might contain:
visitors += 1;
var label = '';
buffer.write('Ali');Each one hides a problem. visitors += 1 is the most common update in programming, yet we
still spell out the 1 every time. label needs to be 'Pass' or 'Fail' depending on a
mark — and with the operators we know so far, we can calculate mark >= 50, but we cannot turn
that bool into one of two words. And if we call write ten times on the same object, we type
its name ten times. This lecture gives each problem its own operator.
Learning Objectives
By the end of this lecture you will be able to:
- Recognize unary operators, which work on a single value
- Use
++and--to add or subtract one - Explain the difference between prefix (
++a) and postfix (a++) forms - Choose between two values with the conditional expression
condition ? a : b - Use a conditional expression inside assignments and string interpolation
- Build text step by step with a
StringBuffer - Call several methods on one object with the cascade operator
.. - Use the null-aware cascade
?..on a value that might benull
1. Operators That Work on One Value
Most operators you have met sit between two values: price * quantity, mark >= 50.
A unary operator works on just one value. You already know two of them:
void main() {
var balance = 250;
var isOpen = false;
print(-balance);
print(!isOpen);
}-250
true-balance gives the negative of balance, and !isOpen flips a bool. Neither one changes
the variable — balance is still 250 afterwards. The operators in the next sections are
different: they change the variable itself.
2. The Increment Operator ++
++ adds one to a variable and stores the result back in it.
Analogy — think of a tally counter, the little clicker a doorman holds. Each click adds exactly one to the number on the display.
++is one click.
void main() {
var visitors = 0;
visitors++;
visitors++;
visitors++;
print(visitors);
}3visitors++ means exactly the same as visitors += 1 or visitors = visitors + 1 — it is
simply the shortest way to write it.
3. The Decrement Operator --
-- is the opposite: it subtracts one and stores the result back.
void main() {
var ticketsLeft = 5;
ticketsLeft--;
ticketsLeft--;
print(ticketsLeft);
}3Both operators work on double variables too. They still change the value by exactly one:
void main() {
var temperature = 25.5;
temperature++;
print(temperature);
temperature--;
temperature--;
print(temperature);
}26.5
24.54. Prefix and Postfix
You can write the operator after the variable (score++, called postfix) or before
it (++score, called prefix). On a line of its own, there is no difference at all:
void main() {
var a = 10;
var b = 10;
a++;
++b;
print(a);
print(b);
}11
11The difference appears only when you use the result of the operation in the same
expression — for example inside print().
5. The Value an Increment Gives Back
An increment does two jobs: it changes the variable, and it produces a value, like any other
expression. The position of ++ decides which value you get:
- Postfix
a++— gives back the old value, then adds one. - Prefix
++a— adds one first, then gives back the new value.
void main() {
var a = 5;
print(a++);
print(a);
var b = 5;
print(++b);
print(b);
}5
6
6
6print(a++) printed 5, the old value — but a had still become 6, as the next line
shows. print(++b) added one first, so it printed 6 straight away. A simple way to remember
it: read from left to right. In a++ you meet the variable first, so you get it before the
change; in ++a you meet the ++ first, so the change happens first.
6. Tracing Prefix and Postfix Step by Step
When the result is stored in another variable, trace the lines one at a time:
void main() {
var count = 1;
var first = count++; // first gets 1, then count becomes 2
var second = ++count; // count becomes 3, then second gets 3
print('first: $first, second: $second, count: $count');
}first: 1, second: 3, count: 3This works, but it makes the reader stop and think. In real code, put ++ and -- on a line
of their own whenever you can, so the prefix/postfix question never comes up.
7. Where ++ and -- Cannot Be Used
The operator stores a new value, so it needs a variable that can be changed and that
holds a number. A final variable cannot be changed:
void main() {
final count = 1;
count++; // ✗
}Error: Can't assign to the final variable 'count'.
count++; // ✗
^^^^^A nullable number is rejected as well, because adding one to null makes no sense:
void main() {
int? score;
score++; // ✗
}Error: Operator '+' cannot be called on 'int?' because it is potentially null.
score++; // ✗
^
Error: A value of type 'num' can't be assigned to a variable of type 'int?'.
score++; // ✗
^Notice that both messages talk about adding and assigning: Dart treats score++ as
score = score + 1, and it refuses that + on a value that might be null.
8. The Conditional Expression ? :
A conditional expression chooses one of two values based on a bool:
condition ? valueIfTrue : valueIfFalsevoid main() {
var mark = 72;
var result = mark >= 50 ? 'Pass' : 'Fail';
print(result);
}PassRead it as a question: "Is mark >= 50? If yes, 'Pass'; otherwise, 'Fail'." Because it
uses three parts, many programmers call it the ternary operator. The condition can be any
bool expression — including the comparison and logical operators from Lectures 018 and 019.
9. It Is an Expression, So It Fits Anywhere
A conditional expression produces a value, so you can put it wherever a value is expected —
for example straight inside interpolation with ${...}:
void main() {
var items = 1;
var balance = -40;
print('You have $items ${items == 1 ? 'item' : 'items'}.');
print('Account: ${balance < 0 ? 'overdrawn' : 'in credit'}');
}You have 1 item.
Account: overdrawnOnly the chosen side is ever worked out; the other side is skipped completely. Choosing between
whole blocks of statements — not just two values — is the job of if, which starts in
Lecture 022. Lecture 023 returns to conditional expressions and compares the two.
10. Both Sides Should Have the Same Type
The variable that receives the result gets the type that fits both sides. When both sides
are String, the result is a String. Mixing types leads to a wider type:
void main() {
var points = 120;
var isMember = points >= 100;
var mixed = isMember ? 10 : 2.5;
var decimals = isMember ? 10.0 : 2.5;
print(mixed);
print(decimals);
}10
10.0mixed is a num, because one side is an int and the other a double — so it holds the
int 10 here, and would hold the double 2.5 for a non-member. decimals is always a
double, because both sides are. When a value is money or a measurement, give both sides the
same type.
11. Conditional Expression or ???
In Lecture 014 you used ?? to replace a missing value. For that one job, ?? is a shorter
conditional expression:
void main() {
var input = '';
String? nickname = input.isEmpty ? null : input;
var shown1 = nickname != null ? nickname : 'Guest';
var shown2 = nickname ?? 'Guest';
print(shown1);
print(shown2);
}Guest
GuestBoth lines give the same answer. Use ?? when the question is "is it null?"; use ? :
for every other question, such as mark >= 50.
12. Building Text with a StringBuffer
Before the cascade operator, we need an object worth calling methods on. A StringBuffer
collects text piece by piece, and gives you the finished String at the end:
void main() {
var receipt = StringBuffer();
receipt.write('Notebook');
receipt.write(' x 2');
receipt.writeln();
receipt.write('Total: 1000');
print(receipt.toString());
}Notebook x 2
Total: 1000StringBuffer() creates a new, empty buffer. write adds text, writeln adds text followed
by a line break, and toString() returns everything collected so far. Unlike a String, which
never changes (Lecture 008), a buffer is changed by each write. The parentheses in
StringBuffer() create a new object; how that works is the topic of Lecture 053, Creating
Classes & Objects.
13. The Cascade Operator ..
Notice how often receipt. was repeated above. The cascade operator .. lets you perform
several operations on the same object, writing its name only once:
void main() {
var receipt = StringBuffer();
receipt
..write('Notebook')
..write(' x 2')
..writeln()
..write('Total: 1000');
print(receipt);
}Notebook x 2
Total: 1000Each .. means "and on that same object, also do this". Dart formats a cascade with one
operation per line, which reads like a checklist. print(receipt) works without
toString(), because print converts the buffer to text by itself.
14. A Cascade Gives Back the Object, Not the Result
This is the most important rule about cascades. A normal method call x.method() gives back
the method's result. A cascade x..method() throws that result away and gives back
x itself:
void main() {
var word = 'hello';
var normal = word.toUpperCase();
var cascade = word..toUpperCase();
print(normal);
print(cascade);
}HELLO
helloword.toUpperCase() returned a new string, 'HELLO'. With .. the new string was created and
then discarded, and the cascade handed back word — still 'hello'. Cascades are therefore
useful for methods that change an object, like write, not for methods that return a new
value.
15. Creating and Filling an Object in One Step
Because a cascade gives back the object, you can create a buffer, fill it, and store it — all in one statement:
void main() {
var greeting = StringBuffer()
..write('Hello, ')
..write('Sara')
..write('!');
print(greeting);
print(greeting.length);
}Hello, Sara!
12StringBuffer() creates the buffer, the three cascades fill it, and the whole expression's
value — the buffer itself — goes into greeting. length counts the characters collected so
far. Cascades become even more useful with lists (Lecture 039) and with your own classes
(Lecture 053), where you will also use them to set properties.
16. The Null-Aware Cascade ?..
If the object might be null, a plain .. would be rejected, just as a plain . is. Start
the cascade with ?.. instead. If the object is null, the whole cascade is skipped;
otherwise it runs normally:
void main() {
var answer = 'no';
var wantsNotes = answer == 'yes';
StringBuffer? notes = wantsNotes ? StringBuffer() : null;
notes
?..write('Bring ID')
..write(' and a pen');
print(notes);
}nullnotes is null, so neither write ran and nothing crashed. Write ?.. only on the
first operation; if the object is null, the following .. operations are skipped too.
It is the cascade version of the ?. operator from Lecture 014.
17. A Real-World Example
void main() {
var customer = 'Ali';
var price = 400;
var quantity = 2;
var itemsScanned = 0;
itemsScanned++;
itemsScanned++;
var total = price * quantity;
var delivery = total >= 500 ? 0 : 150;
var receipt = StringBuffer()
..writeln('Customer: $customer')
..writeln('Items scanned: $itemsScanned')
..writeln('Total: $total')
..write('Delivery: ${delivery == 0 ? 'free' : delivery}');
print(receipt);
}Customer: Ali
Items scanned: 2
Total: 800
Delivery: free++ counts the items, a conditional expression decides the delivery charge (and how to show
it), and a cascade builds the receipt in one statement.
Code Examples
Example 1 — Counting Up
void main() {
var likes = 10;
likes++;
likes++;
print('Likes: $likes');
}Likes: 12Example 2 — Counting Down
void main() {
var lives = 3;
lives--;
print('Lives left: $lives');
lives--;
print('Lives left: $lives');
}Lives left: 2
Lives left: 1Example 3 — Prefix vs Postfix Side by Side
void main() {
var x = 7;
var y = 7;
print('x++ gives ${x++}, x is now $x');
print('++y gives ${++y}, y is now $y');
}x++ gives 7, x is now 8
++y gives 8, y is now 8Example 4 — Choosing a Word
void main() {
var age = 15;
var group = age >= 18 ? 'adult' : 'child';
print('Zayan is a $group.');
}Zayan is a child.Example 5 — Conditional Inside Interpolation
void main() {
var messages = 3;
print('$messages new ${messages == 1 ? 'message' : 'messages'}');
}3 new messagesExample 6 — Building Text with a Cascade
void main() {
var card = StringBuffer()
..writeln('Name: Sara')
..writeln('Age: 18')
..write('City: Lahore');
print(card);
}Name: Sara
Age: 18
City: LahoreExample 7 — Student Result
void main() {
var math = 80;
var science = 45;
var english = 62;
var passed = 0;
passed += math >= 50 ? 1 : 0;
passed += science >= 50 ? 1 : 0;
passed += english >= 50 ? 1 : 0;
var report = StringBuffer()
..writeln('Student: Ali')
..writeln('Science: ${science >= 50 ? 'Pass' : 'Fail'}')
..write('Subjects passed: $passed of 3');
print(report);
}Student: Ali
Science: Fail
Subjects passed: 2 of 3How it works
- Each
? 1 : 0turns a comparison into a number:1for a pass,0for a fail. +=adds that number topassed, which ends at1 + 0 + 1, or2.- Inside the report, another conditional expression turns
science >= 50into a word. - One cascade builds the whole report, and one
printshows it.
Example 8 — Shopping Total
void main() {
var price = 300;
var quantity = 0;
quantity++;
quantity++;
quantity++;
var subtotal = price * quantity;
var discount = subtotal >= 1000 ? 100 : 0;
var total = subtotal - discount;
print('Quantity: $quantity');
print('Subtotal: $subtotal');
print('Discount: $discount');
print('Total: $total');
}Quantity: 3
Subtotal: 900
Discount: 0
Total: 900How it works
- Three
++lines add one item each, soquantitybecomes3. 300 * 3gives a subtotal of900.900 >= 1000isfalse, so the conditional expression picks0— no discount.- Try changing
priceto400: the subtotal becomes1200and the discount100.
Example 9 — Bank Balance
void main() {
var balance = 1000;
var withdrawal = 1500;
var transactions = 0;
var allowed = withdrawal <= balance;
balance -= allowed ? withdrawal : 0;
transactions++;
print(allowed ? 'Withdrawal approved' : 'Insufficient funds');
print('Balance: $balance');
print('Transactions: $transactions');
}Insufficient funds
Balance: 1000
Transactions: 1How it works
allowedstores the answer to "is there enough money?" — herefalse.- The conditional expression subtracts
withdrawalonly when allowed, otherwise0, so the balance stays at1000. transactions++records the attempt either way.- A second conditional expression picks the message to print.
Example 10 — Temperature Report
void main() {
var celsius = 29.5;
celsius++;
celsius++;
var feel = celsius >= 30 ? 'hot' : 'pleasant';
var report = StringBuffer()
..write('Afternoon: $celsius °C')
..write(' ($feel)');
print(report);
}Afternoon: 31.5 °C (hot)How it works
celsiusis adouble, and each++still adds exactly1:29.5→30.5→31.5.31.5 >= 30istrue, sofeelbecomes'hot'.- The cascade writes two pieces into one buffer, which
printshows as one line.
Common Beginner Mistakes
1. Using ++ on a final variable
// final count = 1;
// count++;Error: Can't assign to the final variable 'count'.++ changes the variable, so it must be declared with var or a type, not final or const.
2. Expecting print(a++) to print the new value
void main() {
var a = 5;
print(a++);
}5Postfix gives back the old value. Increment on its own line first, then print.
3. Writing x = x++
void main() {
var x = 5;
x = x++;
print(x);
}5No error — and no change. x++ adds one to x, but gives back the old 5, which the
assignment then stores straight back into x. Write just x++;.
4. Forgetting the : part
// var mark = 72;
// print(mark >= 50 ? 'Pass');Error: Expected ':' before this.
print(mark >= 50 ? 'Pass');
^A conditional expression always needs both values — there must be an answer for "no".
5. Storing the result of a method that returns nothing
// var receipt = StringBuffer().write('Notebook');
// print(receipt);Error: This expression has type 'void' and can't be used.
print(receipt);
^write gives back nothing (void), so receipt holds nothing usable. Use a cascade,
StringBuffer()..write('Notebook'), which gives back the buffer itself.
6. Using a cascade to get a method's result
word..toUpperCase() gives back word, not the upper-case text (section 14). Use a normal
. when you need what the method returns.
7. Nesting conditional expressions too deeply
a ? x : b ? y : z is legal, but hard to read. When a choice has more than two outcomes, wait
for if, else if, and switch in Lectures 022–025.
Best Practices
- Put
++and--on a line of their own; avoid using their result inside other expressions. - Prefer
count++for "add one" and+=for any other amount. - Use a conditional expression to choose between two values, not to run actions.
- Keep both sides of
? :the same type. - Use
??instead ofx != null ? x : fallback. - Use cascades for a series of changes to one object — write one operation per line.
- Start a cascade with
?..only when the object's type is nullable.
Mini Challenge
Store a player's name 'Sara', a starting score of 9, and a target of 10. The player scores
one more point (use ++). Build a message with a StringBuffer and a cascade, and use a
conditional expression to show 'Level complete!' if the score reached the target, or
'Keep going!' otherwise.
Your program should print exactly:
Player: Sara
Score: 10
Level complete!Try it yourself before opening the solution.
Show solution
void main() {
var player = 'Sara';
var score = 9;
var target = 10;
score++;
var message = StringBuffer()
..writeln('Player: $player')
..writeln('Score: $score')
..write(score >= target ? 'Level complete!' : 'Keep going!');
print(message);
}Player: Sara
Score: 10
Level complete!How it works
score++changes the score from9to10.- The cascade creates the buffer and adds three pieces of text to it.
10 >= 10istrue, so the conditional expression writes'Level complete!'.
Practice Questions
Work through these in order — they run from beginner to application to challenge. Every question gives you exact values, so you can compare your output with the answers.
Beginner
- Create
var steps = 99;, increase it by one with++, and print it. - Create
var stock = 4;, decrease it by one with--twice, and print it. - Create
var age = 20;and use a conditional expression to print'Adult'ifageis at least18, otherwise'Minor'.
Application
- Without running it first, predict what this prints, then check:
var n = 3; print(n++); print(n); print(++n); - Store
var count = 1;and print1 appleorN appleswith oneprintand a conditional expression inside the interpolation. Then changecountto5and run it again. - Store
var balance = -200;and printStatus: overdrawnwhen the balance is below zero, otherwiseStatus: OK. - Use one
StringBufferand a cascade to build these three lines, then print the buffer:Name: Zayan,Age: 6,Grade: 1. - Store
var celsius = 18.5;, raise it by one with++three times, and printIt is 21.5 °C, warm— the word must bewarmif the temperature is at least20, otherwisecool.
Challenge
- Store three marks —
90,40,55— and a countervar passed = 0;. Using only+=and conditional expressions (noif), count how many marks are at least50, then printPassed: 2. - Create
var code = '';, thenString? coupon = code.isEmpty ? null : code;andvar total = 1000;. Print the amount to pay, which is900when there is a coupon and1000when there is not, using a conditional expression. Then createStringBuffer? log;built ascoupon != null ? StringBuffer() : null, write'Coupon used'into it with a null-aware cascade, and printlog.
Practice Answers
Question 1 — Solution
void main() {
var steps = 99;
steps++;
print(steps);
}100Question 2 — Solution
void main() {
var stock = 4;
stock--;
stock--;
print(stock);
}2Question 3 — Solution
void main() {
var age = 20;
print(age >= 18 ? 'Adult' : 'Minor');
}AdultQuestion 4 — Solution
void main() {
var n = 3;
print(n++);
print(n);
print(++n);
}3
4
5n++ prints the old value 3 and leaves n at 4. ++n adds one first, so it prints 5.
Question 5 — Solution
void main() {
var count = 1;
print('$count ${count == 1 ? 'apple' : 'apples'}');
}1 appleWith count = 5, the same program prints 5 apples.
Question 6 — Solution
void main() {
var balance = -200;
print('Status: ${balance < 0 ? 'overdrawn' : 'OK'}');
}Status: overdrawnQuestion 7 — Solution
void main() {
var info = StringBuffer()
..writeln('Name: Zayan')
..writeln('Age: 6')
..write('Grade: 1');
print(info);
}Name: Zayan
Age: 6
Grade: 1Question 8 — Solution
void main() {
var celsius = 18.5;
celsius++;
celsius++;
celsius++;
print('It is $celsius °C, ${celsius >= 20 ? 'warm' : 'cool'}');
}It is 21.5 °C, warmQuestion 9 — Solution
void main() {
var math = 90;
var science = 40;
var english = 55;
var passed = 0;
passed += math >= 50 ? 1 : 0;
passed += science >= 50 ? 1 : 0;
passed += english >= 50 ? 1 : 0;
print('Passed: $passed');
}Passed: 2Each conditional expression turns a comparison into 1 or 0, and += adds them up:
1 + 0 + 1.
Question 10 — Solution
void main() {
var code = '';
String? coupon = code.isEmpty ? null : code;
var total = 1000;
var toPay = coupon != null ? total - 100 : total;
print(toPay);
StringBuffer? log = coupon != null ? StringBuffer() : null;
log?..write('Coupon used');
print(log);
}1000
nullThere is no coupon, so the conditional expression picks total, and log is null. The
?.. cascade sees null and skips the write without crashing, so print(log) shows null.
Quick Revision
- What is a unary operator? Give two examples.
- What does
count++do tocount? - What value does
a++give back? What does++agive back? - Why does
x = x++leavexunchanged? - What are the three parts of a conditional expression?
- When should you use
??instead of? :? - What does a cascade
x..method()give back? - When do you need
?..instead of..?
Key Takeaways
- A unary operator works on one value:
-x,!flag,x++,x--. ++adds one and--subtracts one, storing the result back — onintanddoublevariables that can be changed.- Postfix
a++gives back the old value; prefix++agives back the new one. On a line of its own, there is no difference. condition ? a : bchooses between two values; only the chosen side is worked out.- A conditional expression is a value, so it fits in assignments and in
${...}. - A
StringBuffercollects text withwriteandwriteln. - The cascade
..performs several operations on one object and gives back the object, not the method's result. ?..skips the whole cascade when the object isnull.
What's Next?
Lecture 21 — Operator Precedence & Custom Operators. We now know almost every operator
Dart has. But when several of them meet in one expression — price + tax * 2 > 100 && isMember ? 'free' : 'paid' — which runs first? Next we learn Dart's precedence rules, how brackets
override them, and get a first look at how types define their own operators.