The Processor Class

Last but not least is the file that does the heavy lifting, processor.dart:

The Processor class is where most of the logic exists for handling keypad interactions and performing the actual calculations. To begin, we have four basic properties on the class for storing input and output variables:

_operator (holds the current requested mathematical operator)

_valA (holds the operand left of the operator)

_valB (holds the operand right of the operator)

_result (holds the result of the previous calculation)

Also within this class is another implementation of Stream / StreamController which the Calculator class uses to subscribe to messages that contain the updated display value. The main difference here is that the _fire method is declared with an underscore prefix, making it private to this class, which is desired since this is the only place where the actual result value is calculated. There is also a convenience method refresh which fires the current output value downstream for listening components to set state and update view. This method is made public to allow listening components to request an update, for example when some initialization process is complete.

Next up are two accessor properties which determine what to display:

_output (returns the _result if not null , else returns _equation)

, else returns _equation) _equation (returns an equation if _operator is ready, else returns _valA)

This combination of chained accessors provides a simple way to control the calculator output by displaying either the _result of the previous calculation, the calculation that is queued up and ready to be solved, or the left-most (and only) value, depending on what state the calculator is currently in.

This class also has a dispose method to allow graceful shutdown of the app. Next up is the group of four methods that handle incoming key events:

process (chooses the next action to take based on KeySymbol type)

type) handleFunction (prepares to execute some function on the current state)

handleOperator (assigns the selected math operator to _operator)

handleInteger (adds the key’s number value to the appropriate operand)

The process method is where events from the KeyController are forwarded in by the Calculator. This method uses the key symbol’s type to choose whether it should invoke a function, store an operator, or process numerical input.

The handleFunction method first check if the left operand (_valA) is ‘0’ and discard the event if so. It then checks to determine if it should store the result of a previous equation in the leftmost operand by calling _condense to allow the next equation to be entered. Next, a map of functions is used to select the desired function based on the KeySymbol of the incoming KeyEvent, and the KeySymbol is forwarded to the appropriate function followed by a refresh. This is generally cleaner than a giant chain of if-else statements, and this level of expression is a paramount feature of modern languages like Dart.

The handleOperator method also discards events when the left operand is ‘0’ and will also store the result of a previous calculation in the left operand as necessary. It then stores the selected operator in _operator and calls refresh.

The handleInteger method receives numerical input and appends it to either the left-side operand value if no _operator is present, or the right-side operand if _operator has a value. It also calls a refresh when complete.

The next group of methods in the class handle function input from the keypad to modify the state of the processor and it’s values:

_clear (resets the calculator and calls refresh)

_sign (flips the sign of the operand that is currently receiving input)

_percent / calcPercent (divides the value of the current operand by 100)

_decimal (appends a decimal point to the end of the current operand)

_calculate (perform the actual calculation and store the result in _result)

_condense (store _result in the left operand and prepare for more input)

The _clear method simply resets the calculator to zero. The _sign method determines which operand to modify and either prepends it with or removes a - character. The _percent and _decimal methods work largely the same way, choosing the correct operand and performing their respective tasks upon it.

The _calculate method will discard events when there is no operator or right-side value to perform a calculation on, otherwise it utilizes a function map to choose which formula to use, calculates the result, removes any insignifigant trailing zeros, stores the value in _result, and wraps up by calling refresh.