We worried about the expression being malformed (syntax errors), but we didn’t worry about mathematical errors.
When writing a calculator in most programming languages (especially if using integer arithmetic, which isn’t our case), making an app like ours (that doesn’t compute square root or trigonometric functions) leaves us exposed to one thing that could make it crash: dividing by 0.
Dart, when using floating-point arithmetic like in our case, is smart enough to display the word Infinity instead of a result; this is nice because it doesn’t cause the app to crash, but some could argue that it isn’t really the correct answer in most cases: as the divisor approaches 0 the quotient approaches infinity, but division by zero in itself is impossible, so you might choose to display an error if you were making a calculator and were so inclined.
If that is the case, you would need to add a condition to each calculation in Calculation.getResult to check whether there is an attempt at division by 0, throw an exception and catch it in the calling method.
An exception in Dart is defined as a subclass of Exception, in the following way:
| class DivideByZeroException implements Exception { |
| |
| } |
That exception can be thrown by using:
| throw DivideByZeroException(); |
In this specific case, inside the definition of Calculation.getResult in calculator.dart, at the point where we handle divisions (where you find else if(a[i] == "÷") {), we’ll add:
| if(double.parse(a[i+1]) == 0) |
| throw DivideByZeroException(); |
so the part of getResult that handles divisions and multiplications becomes:
| for(int i = 0; i < a.length; i++) { |
| if(a[i] == "x") { |
| a[i-1] = "${double.parse(a[i-1]) * double.parse(a[i+1])}"; |
| a.removeAt(i); |
| a.removeAt(i); |
| i--; |
| } else if(a[i] == "÷") { |
| if(double.parse(a[i+1]) == 0) |
| throw DivideByZeroException(); |
| a[i-1] = "${double.parse(a[i-1]) / double.parse(a[i+1])}"; |
| a.removeAt(i); |
| a.removeAt(i); |
| i--; |
| } |
| } |
Exceptions in Dart are caught using:
| try { |
| code_that_might_generate_exceptions(); |
| } catch(exception) { |
| print("$exception"); |
| } |
This code catches any kind of exception; if you wanted to catch only certain types of exceptions you would use:
| try { |
| code_that_might_generate_exceptions(); |
| } on ExceptionClass catch(exception) { |
| print("$exception"); |
| } |
And if you don’t need to use the value of the exception you would leave out the catch(exception) part:
| try { |
| code_that_might_generate_exceptions(); |
| } on ExceptionClass { |
| print("you did something bad"); |
| } |
In the specific case, the code that might generate exceptions is the _str = _calculation.getResult().toString() function call and assignment and we want to set _str to something like You mustn’t divide by 0.
The getResult method will therefore become:
| void getResult() { |
| setState(() { |
| try { |
| _str = _calculation.getResult().toString(); |
| } |
| on DivideByZeroException { |
| _str = "You mustn't divide by 0"; |
| } finally { |
| _calculation = new Calculation(); |
| } |
| }); |
| } |
Adding a finally clause to reset the calculation regardless of whether or not the exception was thrown.