Dart from the ground up

Decisions: if, else and switch

Choosing a path with if and else, picking one of two values with a short expression, and naming the cases of a choice with switch and an enum.

Part 4 of 12

Beginner17 min readBack to Courses

One question, two roads

An if runs a block of lines only when a condition is true. The condition lives in parentheses and must be a bool: if (score >= 90) { ... }. When the condition is false the block is skipped and the program carries on below it.

else covers the other case, and else if chains more questions in order. Dart reads the chain from the top and takes the first match, so the order you write the conditions in is the order they are asked. Whatever the chain does, the program always leaves it at exactly one place.

Comparing, combining, and the short form

The comparisons are > and <, >= and <=, and == for equality; a single equals sign assigns and a double one asks. Combine them with && for both, || for either, and ! to turn a true into a false.

When a choice only picks a value, the short form is easier to read: score >= 70 ? "pass" : "retry". Everything before the question mark is the condition, and the two values after it are the answers for true and false, in that order.

switch, and the enum behind it

switch takes one value and runs the case that matches it. Each case ends with break so the following cases are not run as well, and default catches anything the named cases did not cover. It is the tidier shape when one value has several possible answers.

switch is happiest over an enum. An enum is a named, fixed list of options: enum Grade { pass, fail } invents a type with exactly those two values, and Grade.pass is one of them. Because the options are written down in one place, a switch over an enum can be reasoned about completely — and adding an option later is the reminder to visit every switch that has to handle it.

Conditions are values, and they stop early

Every condition is an expression that produces true or false, and if simply looks at the answer. Comparisons produce bools, and && and || combine them. && is the stricter one: both sides must be true. || is satisfied by either.

Both operators stop as soon as the answer is settled. In count > 0 && total ~/ count > 10, the division is never reached when count is zero, which is why that shape is safe. The same short-circuit is why a null check on the left of && protects the read on the right.

Precedence matters when the operators are mixed: comparisons bind tighter than &&, and && binds tighter than ||. When a reader would have to think about it, put the parentheses in. They cost two characters and remove the doubt.

Choosing between an if chain and a switch

An if chain is for questions, especially questions about different values or ranges. A switch is for one value with a known set of answers. Reading a switch, you can see every option at once; reading an if chain, you have to follow the order.

The order in an if chain is part of the logic. if (score >= 70) placed before if (score >= 90) would send a 95 down the first path, because the first true branch wins. Put the narrowest condition first, or make each branch a range that cannot overlap.

An enum makes the set of answers explicit, and a switch over it can be checked for completeness. When a new enum value is added later, the compiler points at every switch that has not handled it — a reminder no if chain can give you.

An if, a short form and a switch

void main() {
  final score = 72;
  if (score >= 70) {
    print("Pass");
  } else {
    print("Try again");
  }
  print(score > 50 ? "over half" : "under half");
  switch (score ~/ 10) {
    case 9:
      print("Nine");
      break;
    case 7:
      print("Seven");
      break;
    default:
      print("Other");
      break;
  }
}

A grade chain, a short form and an enum switch

Run it and change the score so that a different branch runs. Then add a case to the enum and to the switch for it, and print the result so you can see both changes in the output.

Output
Click "Run" to execute your code

Three decision mistakes. Writing if (count = 0), which assigns and then complains that the result is not a bool. Forgetting break in a switch case, so the next case runs too. And writing if (count) where count is a number, when Dart wants an actual comparison such as count != 0.

Try these next

  • Change the score in the example to 90 and then to 69, and name the branch that runs each time.
  • Add a fourth enum value and let the compiler show you every switch that now needs a case.
  • Rewrite one if and else pair as the short form and check that the output is the same.

Shipping bands from weight and size

Run it and match each parcel to the branch that priced it. Then change the express flag and the list of weights, and add a size of your own to the enum and the switch that uses it.

Output
Click "Run" to execute your code

A forgotten break falls through into the next case and prints two lines where you expected one. If output ever looks doubled, look at the ends of your cases first.

Check what you learned

One question at a time. A wrong answer explains itself — take another go.

Part 1 of 5

Why does a case end with break?