Learning Flex Basics (Part 2): Creating a Simple Calculator with Macromedia Flex
In this tutorial, you will build a working calculator in Macromedia Flex. Although the calculator is simple—it just adds, subtracts, multiplies, and divides—building it will enable you to work with several essential building blocks for Flex applications.
This tutorial is the first part in a four-part series written by Robert Crooks of the Macromedia Customer Training group:
- Learning Flex Basics (Part 1): Creating Your First Flex Application
- Learning Flex Basics (Part 3): Working with Containers
- Learning Flex Basics s(Part 4): Working with Data Models
If you like what you see and want to get more experience with Flex, Macromedia Customer Training offers Fast Track to Macromedia Flex, a two-day onsite training course to jump-start Flex development in your organization. Now let's get started!
What You Will Learn
- How to use the Application tag.
- How to use a Panel container.
- How to use Label controls.
- How to use Button controls.
- How to use a Grid container.
- How to create an ActionScript controller class for an MXML file.
Requirements
To complete this tutorial you will need to install the following software and files:
Macromedia Flex
(Installed and running on any server and platform supported by Flex. Note that the Flex trial is available on CD from the Macromedia store for $8.99. To learn more about the Flex trial, please read the Flex Frequently Asked Questions.)
Dreamweaver MX 2004 or a text editor that you can use to write XML and ActionScript code (a basic text editor such as Notepad is adequate)
Solution files for the tutorial:
- calculator.mxml and CalculatorController.as from the zip for this article.
Prerequisite knowledge:
- Read A Brief Overview of Flex.
- You must know how to browse a Flex application.
- You should have completed Learning Flex Basics (Part 1): Creating Your First Flex Application, or have a basic understanding of how Flex applications work.
Building the Calculator
The calculator will have two parts: a view built in MXML; and a controller built as an ActionScript class. The view will contain all the visual elements of the calculator such as the display and buttons. The controller contains data that holds variables you need in order to keep track of the user's actions; it also contains methods that you will use to handle events, such as the user clicking a button.

Figure 1. The calculator you will build in this tutorial.
In building the calculator you will learn to use:
- The Application class
- The Panel container
- The Label element
- The Grid container
- The Button element
- An ActionScript class that includes:
- A class definition
- Properties
- Methods
- The if-else and switch-case control structures
Create the Application Object
To begin any Flex application, start with an XML declaration and an Application
tag
block. The Application tag include a namespace declaration for the MX
class library: xmlns:mx="http://www.macromedia.com/2003/mxml"
. The mx prefix
is used with all tags for this library.
- Create a new file and save it as calculator.mxml in the flex_tutorials directory.
- At the beginning of the file, insert the XML declaration:
<?xml version="1.0">
-
After the XML declaration, add an
Application
tag with the MXML namespace:<mx:Application xmlns:mx="http://www.macromedia.com/2003/mxml"> </mx:Application>
Add a Panel for the Calculator
Generally, you place the visual components of your Flex application
inside containers,
which provide bounding boxes for text, controls, images, and other media
elements. Here, you will use a Panel container that provides
the overall visual wrapper for most applications. You will also use the
title
property
of Panel, which provides title text to display in a title bar, automatically
included at the top of the panel.
-
Inside the Application block, add a Panel block with the title property equal to Calculator:
<mx:Panel title="Calculator"> </mx:Panel>
Add the Calculator Display
The Label element displays a single line of text. It has a number of properties; here, you will use the following properties:
- id: a unique identifier used to reference this instance in an event handler
- width: width of label in pixels
- text: text to display
- textAlign: horizontal alignment of text: left | right | center
-
Inside the Panel block, add a Label with the properties as follows:
<mx:Label id="calcDisplay" width="150" textAlign="right" />
Note: There is no text value in this tag because you will set the value with handler methods in the controller class; you build that functionality later in the tutorial.
Add a Grid to Lay Out the Buttons
To lay out the calculator buttons, you use a Grid layout
container. A Grid container is similar to an HTML table: It arranges information
in rows and columns using nested GridRow and GridItem containers. GridItem
containers have
colSpan
and rowSpan
properties
that work the same as the HTML <td>
attributes. You are
going to lay out the buttons for the calculator in the grid. If you look
at the screen shot of the finished calculator on page
2, you will see that
you need five rows, and that the top and bottom rows will contain three
buttons, while the middle three rows contain four.
-
After the Label element, but still inside the Panel, add a Grid container block:
<mx:Grid> </mx:Grid>
-
Inside the Grid container block, add five GridRow container blocks:
<mx:GridRow> </mx:GridRow><mx:GridRow> </mx:GridRow><mx:GridRow> </mx:GridRow><mx:GridRow> </mx:GridRow><mx:GridRow> </mx:GridRow>
-
Inside the first GridRow container , add three GridItem containers, and set the
colSpan
of the first item to 2:<mx:GridItem colSpan="2"> </mx:GridItem><mx:GridItem> </mx:GridItem><mx:GridItem> </mx:GridItem>
-
Inside each of the second, third, and fourth GridRow containers, add four GridItem containers:
<mx:GridItem> </mx:GridItem><mx:GridItem> </mx:GridItem><mx:GridItem> </mx:GridItem><mx:GridItem> </mx:GridItem>
-
Inside the fifth GridRow container , add three GridItem containers, and set the
colSpan
of the third item to 2:<mx:GridItem> </mx:GridItem><mx:GridItem> </mx:GridItem><mx:GridItem colSpan="2"> </mx:GridItem>
Add the Calculator Buttons
The Flex Button control is similar to buttons in HTML and other languages. You will use three properties for the buttons:
- label: the text displayed on the button
- width
- click: the name of the handler executed when the user clicks on the button
Each button in your calculator will have the same general syntax:
<mx:Button label="[something]" width="[number]" click="[some handler method]"/>
Note: The following steps contain the names of event handlers that don't exist yet; you will create the handlers later as part of the CalculatorController class.
-
Add a Button element inside each of the 18 GridItem containers; in the order of the GridItem containers, the Buttons will have the following properties:
Row 1
width="70" label="Clear" click="calcController.clearAll()"width="30" label="C/E" click="calcController.clearEntry()"width="30" label="+" click="calcController.setOperation('add')"
Row 2
width="30" label="1" click="calcController.addNumber('1')"width="30" label="2" click="calcController.addNumber('2')"width="30" label="3" click="calcController.addNumber('3')"width="30" label="-" click="calcController.setOperation('subtract')"
Row 3
width="30" label="4" click="calcController.addNumber('4')"width="30" label="5" click="calcController.addNumber('5')"width="30" label="6" click="calcController.addNumber('6')"width="30" label="*" click="calcController.setOperation('multiply')"
Row 4
width="30" label="7" click="calcController.addNumber('7')"width="30" label="8" click="calcController.addNumber('8')"width="30" label="9" click="calcController.addNumber('9')"width="30" label="/" click="calcController.setOperation('divide')"
Row 5
width="30" label="0" click="calcController.addNumber('0')"width="30" label="." click="calcController.addNumber('.')"width="70" label="=" click="calcController.doOperation()"
- Save the file.
Create the CalculatorController Class
Create an ActionScript class that will be a controller for the calculator. Because the application is simple, you will include the data model in the controller rather than create it separately.
Creating a class in ActionScript is simple and is similar to the way you create one in other object-oriented languages. The basic syntax is:
class ClassName { [properties and methods] }
Save classes as .as files; the file name must be the same as the class name.
Although the compiler will generate an empty constructor if the class definition
does not contain one, it is good practice to include a constructor, even if
it does nothing. Constructor methods in ActionScript may not take parameters
or return any values, and constructor overloading is not supported. Constructors,
like other methods, are created using the function
keyword:
[public|private] function ClassName(){}
Methods (and properties) are public by default, meaning that they can be accessed from outside the class. It's good practice to always make them public or private explicitly as a reminder to make them private if they don't need to be public.
- Open a new empty file and save it as CalculatorController.as in the same directory as your calculator.mxml file.
-
Add a class definition for the CalculatorController class:
class CalculatorController {}
-
Add an empty constructor function inside the class block:
public function CalculatorController(){}
Add Properties to the Controller Class
Look at the code you wrote for calculator.mxml and see how none of it accesses the controller class properties directly; the properties are private, with one exception. When you instantiate the controller class in the MXML application later, you set a property that holds a reference to the entire calculator object, so that you can reference objects such as the display Label in the calculator without having to pass the reference when you call controller methods. So, you will set one public property to hold that reference, and several private properties to handle the calculator events.
The general syntax for setting properties is:
[public|private] var propertyName:dataType[=initialValue];
For example:
private var name:String;
The properties to use, in addition to the public reference to the calculator object, are:
- Two registers to hold temporary values in the calculator pending operations
- A currentRegister value to track the register that the user modifies
- A currentOperation value to track the pending operation
These would be sufficient to make the calculator work, but you will add a few additional properties to hold numerical equivalents of the register values (these must be strings so that you can append additional numbers as the user clicks buttons) and a results value to hold the operation results. These extra properties will simplify some of the code and make the logic clearer.
-
Set the following properties at the beginning of the CalculatorController class block:
Type Name Data Type Initial Value public calcView Object private reg1 String "" (empty string) private reg2 String "" private result Number private currentRegister String "reg1" private operation String "none" private r1 Number private r2 Number
Add Properties to the Controller Class
If you review the values you set for the click events on the calculator buttons, you will see that you need the following public methods in the controller:
doOperation
addNumber
clearEntry
clearAll
setOperation
You will also create three private methods that the controller uses internally:
setDisplay
setCurrentRegister
resetAfterOp
The general syntax for methods is:
[public|private] function methodName(parameter1:dataType...):returnDataType { [ActionScript statements] }
None of the controller methods will return values, so the return data type will be Void for each of them.
Add the doOperation() Method
The doOperation()
method performs addition, subtraction,
multiplication, or division depending on which operation the user requests.
Before carrying the operation, set the r1
and r2
properties equal
to the reg1
and reg2
values, respectively, cast to the Number data type
using the Number()
method, built into ActionScript.
To choose the appropriate operation, you will use a switch-case construct. The general syntax for switch-case in ActionScript is:
switch (expression) { case value: [ActionScript statements] break; // without this you fall through to next case [more cases] default: [default case actions] }
The other bit of ActionScript, the operators for addition, subtraction, multiplication, and division are (and you can probably guess):
+-*/
Use the following steps to add the operation()
method.
-
After the constructor method, create a public
doOperation
method that takes no parameters and returns nothing:public function doOperation():Void { }
-
At the beginning of the method block, set
this.r1
andthis.r2
equal to the respective values ofreg1
andreg2
cast to the Number type:this.r1=Number(reg1); this.r2=Number(reg2);
-
Add a switch-case structure that switches on the value of the
operation
property:switch (operation) { }
-
Inside the switch block, add four cases for the operation types:
add
,subtract
,multiply
, anddivide
. In the each case, set the result property equal to the appropriate operation onr1
andr2,
then call theresetAfterOp()
method, and close each case with abreak
statement:case "add": this.result=r1+r2; resetAfterOp(); break; case "subtract": this.result=r1-r2; resetAfterOp(); break; case "multiply": this.result=r1*r2; resetAfterOp(); break; case "divide": this.result=r1/r2; resetAfterOp(); break;
-
Add a
default
case that does nothing (in case no operation is set):default: // do nothing
Add the addNumber() Method
The addNumber()
method will take a number in string form passed from the
calculator view and append it to the string value for the current register.
The + operator in ActionScript is overloaded (the only operation that is)
and performs string concatenation if both the operands are strings.
The concatenation is simple, but the method needs to take care of a couple of a special case: After the method performs the operation, it copies the result to register 1, and sets register 2 as the current register in case the user wants to perform further operations on the result. If the user starts entering numbers rather than setting a new operation, however, you clear register 1 and set it as the current register.
To check for this condition and perform the correct action, you must use
an if
structure.
You will also need to check two conditions, so you need to know that the
logical AND
operator in ActionScript is &&
(the
logical OR
operator is ||
). The general syntax for if-else structures
is:
if (condition[s]) { [ActionScript statements] }[else if (condition) // 0 or more { [ActionScript statements] }else // 0 or one { [ActionScript statements] }]
The comparison operators for ActionScript are common ones:
Operator | Meaning |
---|---|
== | equals |
< | less than |
> | greater than |
<= | less than or equal |
>= | greater than or equal |
! | logical NOT |
An additional ActionScript concept you need for this method is dynamic evaluation.
Modify the current register, keeping in mind which register is
current is set at runtime by the setCurrentRegister()
method
you will create later. The currentRegister property will always be equal
to the name of one of the register properties, either reg1
or reg2
,
so you can get the current register using the expression this[currentRegister]
.
Square brackets following an object reference forces dynamic evaluation,
and so you can use them to dynamically set or retrieve a property name.
-
After the
doOperation()
method, create a publicaddNumber()
method that takes a String parameter called n and returns nothing:public function addNumber(n:String):Void { }
-
Inside the method block, add an
if
structure that checks for the conditions that theoperation
property is equal to "none" and thecurrentRegister
property is equal to "reg2":if (operation=="none" && currentRegister=="reg2") { }
Note: These conditions are true if the
doOperation()
called theresetAfterOp()
method, and if the user has clicked a number button. -
Inside the if block, set the reg1 property equal to an empty string and then call the
setCurrentRegister()
method (which you create later):reg1=""; setCurrentRegister();
-
After the if structure, append n to the value of the current register:
this[currentRegister]+=n;
Note that you are using a shortcut common to several programming languages here. The statement above is the equivalent of
this[currentRegister] = this[currentRegister] + n;
-
Add a call to the
setDisplay()
method, passing the currentRegister property as an argument (you create this method later):setDisplay(currentRegister);
Add the clearEntry() and clearAll() Methods
These two methods are similar and straightforward, so you'll do them together.
For the
clearEntry()
method, clear the current register and the display.
For the clearAll()
method, clear both registers and the display,
set the pending operation to none,
and set the currentRegister
property to reg1.
-
Add a public
clearEntry()
method that takes no parameters and returns nothing:public function clearEntry():Void { }
-
In the method block, set the value of the current register to an empty string and then call the
setDisplay()
method, passing thecurrentRegister
property as an argument:this[currentRegister]=""; setDisplay(currentRegister);
-
Add a public
clearAll()
method that takes no parameters and returns nothing:public function clearAll():Void { }
-
In the method block, set both register values to empty strings, and then call
setCurrentRegister()
, passing nothing, callsetOperation
, passing "none," and callsetDisplay
, passingcurrentRegister
:reg1=""; reg2=""; setCurrentRegister(); setOperation("none"); setDisplay(currentRegister);
Add setOperation() Method
This is a straightforward setter method. After setting the pending operation,
reset the current register using a setCurrentRegister()
method that you will
create later in the tutorial.
-
Add a public
setOperation()
method that takes a parameter calledoperation
of typeString
and returns nothing:public function setOperation(operation:String):Void { }
-
In the method block, set the value of the
operation
property to the passed operation parameter and then call thesetCurrentRegister()
method:this.operation=operation; setCurrentRegister();
Add setDisplay(), setCurrentRegister(), and resetAfterOp() Methods
The three private methods are also straightforward.
The setDisplay()
method sets the text property of the calculator display label to the value
of the register whose name is passed as a parameter. The setCurrentRegister()
method sets
register 1 as current if it is empty, and otherwise sets register 2 as current. The resetAfterOp()
method copies the result of an operation to register 1 and sets other values to prepare
for further operations.
-
Add a private
setDisplay()
method that takes a parameter calledregister
of typeString
and returns nothing:private function setDisplay(register:String):Void { }
-
In the method block, set the value of the
calcView.calcDisplay.text
property to the passed register parameter:calcView.calcDisplay.text = this[register];
-
Add a private
setCurrentRegister()
method that takes no parameters and returns nothing:private function setCurrentRegister():Void { }
-
In the method block, create an
if-else
structure that sets thecurrentRegister
property to reg1 if reg1's current value is an empty string, and setscurrentRegister
to reg2 otherwise:if (reg1=="") { currentRegister="reg1"; } else { currentRegister="reg2"; }
-
Add a private
resetAfterOp()
method that takes no parameters and returns nothing:private function resetAfterOp():Void { }
-
In the method block, set the value of
reg1
to the current value ofresult
, setsreg2
to an empty string, callssetDisplay()
, passing reg1 as the argument, and callssetOperation()
, passing none as the argument:reg1=String(result); reg2=""; setDisplay("reg1"); setOperation("none");
Save the file.
Note: This completes the CalculatorController.as file.
Instantiate the CalculatorController Class
The final step of the tutorial is to instantiate the CalculatorController
class you just created in the calculator.mxml file. You can do this through
a tag, but since the tag will not be part of the Flex class library, you
will have to add a new namespace for it. You do this by adding an additional xmlns
property
to the Application
tag, giving it a value of "*".
The "*" value simply indicates: "accept any class found
in the application directory as an element."
Also remember that the CalculatorController class needs a reference to the
calculator Application object so that it can access the CalcDisplay object.
You pass this reference by setting the calcView
property of the
CalculatorController object to {this}; in an MXML file, this is
a reference to the overall Application object. The curly braces create data
binding, so that the Flex compiler evaluates the contents as an expression
rather that treating it as a literal string.
Finally, CalculatorController object needs an instance name so that other
elements in the calculator can refer to it; the instance name is the id
attribute
of the tag that instantiates the class.
-
Return to calculator.mxml and add an additional
xmlns
property to theApplication
tag; give the value of the namespace the value of "*" and do not assign a prefix to it:<mx:Application xmlns:mx="http://www.macromedia.com/2003/mxml" xmlns="*">
-
At the beginning of the
Application
tag block, add a CalculatorController tag with the attributesid="calcController"
andcalcView="{this}"
:<CalculatorController id="calcController" calcView="{this}"/>
- Save the file and test it in a web browser (for information on how to browse MXML files, see the first page in this tutorial).
Complete Code Listing for calculator.mxml
<?xml version="1.0"?> <mx:Application xmlns:mx="http://www.macromedia.com/2003/mxml" xmlns="*"> <!-- calculator controller --> <CalculatorController id="calcController" calcView="{this}"/> <!-- calculator view --> <mx:Panel title="Calculator"> <!-- calculator display --> <mx:Label id="calcDisplay" width="150" textAlign="right"/> <!-- calculator controls --> <mx:Grid> <mx:GridRow> <mx:GridItem colSpan="2"> <mx:Button width="70" label="Clear" click="calcController.clearAll()"/> </mx:GridItem> <mx:GridItem> <mx:Button width="30" label="C/E" click="calcController.clearEntry()"/> </mx:GridItem> <mx:GridItem> <mx:Button width="30" label="+" click="calcController.setOperation('add')"/> </mx:GridItem> </mx:GridRow> <mx:GridRow> <mx:GridItem> <mx:Button width="30" label="1" click="calcController.addNumber('1')"/> </mx:GridItem> <mx:GridItem> <mx:Button width="30" label="2" click="calcController.addNumber('2')"/> </mx:GridItem> <mx:GridItem> <mx:Button width="30" label="3" click="calcController.addNumber('3')"/> </mx:GridItem> <mx:GridItem> <mx:Button width="30" label="-" click="calcController.setOperation('subtract')"/> </mx:GridItem> </mx:GridRow> <mx:GridRow> <mx:GridItem> <mx:Button width="30" label="4" click="calcController.addNumber('4')"/> </mx:GridItem> <mx:GridItem> <mx:Button width="30" label="5" click="calcController.addNumber('5')"/> </mx:GridItem> <mx:GridItem> <mx:Button width="30" label="6" click="calcController.addNumber('6')"/> </mx:GridItem> <mx:GridItem> <mx:Button width="30" label="*" click="calcController.setOperation('multiply')"/> </mx:GridItem> </mx:GridRow> <mx:GridRow> <mx:GridItem> <mx:Button width="30" label="7" click="calcController.addNumber('7')"/> </mx:GridItem> <mx:GridItem> <mx:Button width="30" label="8" click="calcController.addNumber('8')"/> </mx:GridItem> <mx:GridItem> <mx:Button width="30" label="9" click="calcController.addNumber('9')"/> </mx:GridItem> <mx:GridItem> <mx:Button width="30" label="/" click="calcController.setOperation('divide')"/> </mx:GridItem> </mx:GridRow> <mx:GridRow> <mx:GridItem> <mx:Button width="30" label="0" click="calcController.addNumber('0')"/> </mx:GridItem> <mx:GridItem > <mx:Button width="30" label="." click="calcController.addNumber('.')"/> </mx:GridItem> <mx:GridItem colSpan="2"> <mx:Button width="70" label="=" click="calcController.doOperation()"/> </mx:GridItem> </mx:GridRow> </mx:Grid> </mx:Panel> </mx:Application>
Complete Code Listing for CalculatorController.as
/* Calculator Controller */ class CalculatorController { // properties // object to hold a reference to the view object public var calcView:Object; // registers to hold temporary values pending operation private var reg1:String=""; private var reg2:String=""; // result of an operation private var result:Number; // the name of the register currently used private var currentRegister:String="reg1"; // the name of the next operation to be performed private var operation:String="none"; // for convenience, holder for numerical equivalents // of the register string values private var r1:Number; private var r2:Number; // constructor public function CalculatorController() {} // methods // perform the current operation on the 2 registers public function doOperation():Void { // cast the register values to numbers r1=Number(reg1); r2=Number(reg2); switch (operation) { case "add": result=r1+r2; resetAfterOp(); break; case "subtract": result=r1-r2; resetAfterOp(); break; case "multiply": result=r1*r2; resetAfterOp(); break; case "divide": result=r1/r2; resetAfterOp(); break; default: // do nothing } } // concatenate number to the value of the current register public function addNumber(n:String):Void { if (operation=="none" && currentRegister=="reg2") { reg1=""; setCurrentRegister(); } this[currentRegister]+=n; setDisplay(currentRegister); } // clear the current register public function clearEntry():Void { this[currentRegister]=""; setDisplay(currentRegister); } // clear both registers and the current operation public function clearAll():Void { reg1=""; reg2=""; setCurrentRegister(); setOperation("none"); setDisplay(currentRegister); } // set the current operation public function setOperation(operation:String):Void { this.operation=operation; setCurrentRegister(); } // set the value shown in the display private function setDisplay(register:String):Void { calcView.calcDisplay.text = this[register]; } // set which register is current private function setCurrentRegister():Void { if (reg1=="") { currentRegister="reg1"; } else { currentRegister="reg2"; } } // reset values after an operation private function resetAfterOp():Void { reg1=String(result); reg2=""; setDisplay("reg1"); setOperation("none"); } }
Where to Go from Here
Although the calculator you have built here is simple, it works, and if you actually need to build a calculator of some kind, you could easily extend this one to include additional functionality. ActionScript includes a static Math class whose methods would do a lot of the work for you. More importantly, though, you should feel comfortable now with basic elements of MXML and ActionScript, and ready to start tackling more sophisticated applications.
Continue on to the following tutorials to learn more:
- Learning Flex Basics (Part 3): Working with Containers
- Learning Flex Basics (Part 4): Working with Data Models
If you like what you see and want to get more experience with Flex, Macromedia Customer Training offers Fast Track to Macromedia Flex, a two-day onsite training course to jump-start Flex development in your organization. You should also look at the informative set of sample applications in included with Flex; these illustrate the use of many Flex features.