by Adobe
Adobe logo


3 May 2011

Building an app with components, states, events, and transitions



This sample project shows you how to create an application interface with Flex. To look at the code, right-click on the SWF in the browser and select View Source or download the sample files and follow the instructions to import the Flash Builder FXP.
MXML and ActionScript
In the main MXML file, you will see two languages used in the code: ActionScript and MXML. ActionScript is an inheritance based object-oriented scripting language based on the ECMAScript standard. MXML is a convenience language; it provides an alternate way to generate ActionScript using a declarative tag-based XML syntax. When you compile an application, the MXML is parsed and converted to ActionScript in memory and then the ActionScript is compiled into bytecode, your SWF, which is rendered by Flash Player. Although you never have to use MXML, it is typically used to define application interfaces (for layouts, the MXML code is usually more succinct and understandable than the corresponding ActionScript would be) and ActionScript is used to write the application logic.
The first line of code in the application is an optional XML declaration tag that specifies how the MXML file is encoded. Many editors let you select from a range of file encoding options. UTF-8 encoding ensures maximum platform compatibility, providing a unique number for every character in a file, and it is platform-, program-, and language-independent.
<?xml version="1.0" encoding="utf-8"?>
After this, you add tags for the various objects you want to add to your application. The MXML must be well-formed: every tag must have an end tag and the value for every tag attribute must be in quotation marks (single or double). Properties can be specified as attributes or as child tags. You usually use child tags when the value is a complex object being defined with MXML.
<Tag attribute="value"/> <Tag> <attribute>value</attribute> <attribute>value2</attribute> </Tag>
The Application tag
The root tag of a Flex web application is the Application tag. When you compile the application, an instance of the Application ActionScript class (which is part of the Flex framework) is created and its properties are set as defined by the attributes in the tag. For this application, the minWidth and minHeight properties of the application are set to 955 and 600 pixels.
<s:Application minWidth="955" minHeight="600" ...>
The three xmlns attributes in the Application tag specify XML namespaces.
<s:Application xmlns:fx="" xmlns:s="library://" xmlns:mx="library://" ...>
The first namespace attribute, xmlns:fx, is used to associate the fx prefix with the use of ActionScript top level language elements (so you can create them with MXML tags, like <fx:Array>) and with compiler tags. Compiler tags do not correspond to ActionScript classes but are used to provide instructions to the compiler. For example, <fx:Script> is used to tell the compiler that the contents inside the tag are going to be ActionScript.
You set an xmlns attribute equal to a string called the URI (universal or uniform resource identifier) that identifies a resource that contains the information about what to do when you use an XML tag with this prefix. The xmlns:fx attribute is set equal to This does not identify a web location but is a mapping to another XML file that maps tag names to corresponding ActionScript classes (for everything besides compiler tags). The mapping for the URI string is located in the flex-config.xml file. The flex-config.xml file contains all the default values used by the compiler when an application is compiled with Flash Builder. You can find the flex-config.xml file in the /Flash Builder/sdks/4.5.0/frameworks/ folder. Inside this file, you will find the following definition:
<namespace> <uri></uri> <manifest>mxml-2009-manifest.xml</manifest> </namespace>
The URI string that was used in the xmlns:fx attribute is here mapped to the mxml-2009-manifest.xml file. If you open this second XML file located in the same directory as the flex-config.xml file, you will see a list of components mapping the names to use in XML tags to the corresponding ActionScript classes:
<componentPackage> <component id="Array" class="Array" lookupOnly="true"/> <component id="Boolean" class="Boolean" lookupOnly="true"/> ...
The flex-config.xml file also includes mappings for the Spark (Flex 4 and later) and MX (Flex 3 and earlier) components in the Flex framework.
<namespace> <uri>library://</uri> <manifest>spark-manifest.xml</manifest> </namespace> <namespace> <uri>library://</uri> <manifest>mx-manifest.xml</manifest> </namespace>
These manifests define the tags that can be used to make instances of the Spark and MX components. For example, you use the Panel tag to create an instance of the ActionScript Panel class located in the spark.components package.
<component id="Panel" class="spark.components.Panel"/>
The Spark and MX manifests are associated with the s and mx namespaces in the application.
<s:Application xmlns:fx="" xmlns:s="library://" xmlns:mx="library://" ...>
MX and Spark components
The Flex framework provides classes for over 100 extensible components (MX and Spark), including UI controls (like the Button, List, HSlider, NumericStepper, DataGrid, and PieChart) and containers (like the VGroup, HGroup, Panel, and Form). The MX component set was included in the Flex 3 and earlier releases and is defined in the mx.* packages. The Spark component set was new for Flex 4 and is defined in the spark.* packages. The Spark components use a new architecture for skinning and have other advantages over the MX components. A lot of the components are included in both the MX and Spark sets, like the Button, TextInput, and List controls. If both exist, you should use the new Spark version of the component. Not all of the components have been rewritten for Spark yet, though, so you might end up with both Spark and MX components (like the DateChooser and the Tree) in an application.
To get familiar with the Spark and MX components, use the Tour de Flex application, browse the spark.components and mx.components packages in the ActionScript 3 Reference for the Adobe Flash Platform (commonly referred to as ASDocs), or browse the source code in the /Flash Builder/sdks/4.5.0/frameworks/projects/ folder.
In the application, you'll see a tag for each control to be added to the interface. Each tag has attributes defined to set values for properties and styles for that instance of that class. You can tell if an attribute is a property or a style by looking at the symbol in front of it in the Flash Builder Code Assist popup or locating it in the Properties or Styles section for the class in ActionScript 3 Reference for the Adobe Flash Platform.
In the application, you'll see instances of the Spark Label, Button, TextInput, and DataGrid controls and the MX LinkButton and Spacer controls.
<s:Label x="20" y="40" text="XYZ Corporation Directory" fontSize="20" fontWeight="bold" color="#1239E3"/>
This code creates an instance of the Label class and sets its x property to 20 pixels and its fontSize style to 20 pixels. When set inline in a tag like this, properties and styles are handled the same as attributes, but they are different entities and must be handled differently in code to make runtime changes. Properties only pertain to this instance of this class whereas styles can be inherited from a parent object or assigned in stylesheets and are managed by a StyleManager class.
You use the id property to assign a component instance a name. You only need to assign instance names to components that are going to be manipulated with ActionScript.
<s:Button id="loginBtn" label="Login" .../>
Where the controls appear in the interface depends upon what container they are placed in and the type of layout that container is using.
A container defines a rectangular region of the Flash Player drawing surface within which you define the components (the children) that you want to appear. Each container uses a set of rules to control the layout of its children, including sizing and positioning, so you don't have to spend time defining it. In this way, Flex helps you more easily build adaptive application interfaces.
Just as with controls, there are both MX and Spark containers and when possible, use the new Spark versions. The main difference between MX and Spark containers is what you can add to them. You can only add Flex components to MX containers. You can add components and new graphics elements (like Rect and Ellipse from the spark.primitives package) to Spark containers. Another difference is that the layout algorithm for MX containers is fixed (so there is a different container for each type of layout), whereas for Spark containers it is selectable (so there is a smaller set of containers and you can switch the layout algorithm for each). Predefined layout classes that can be used with Spark containers include BasicLayout, HorizontalLayout, VerticalLayout, and TileLayout. You can also define your own custom layouts.
The root of the application is a single container, the Application container, that represents the entire Flash Player drawing surface. By default, it uses a BasicLayout which uses absolute positioning in which you explicitly position all container children or use constraints (which specify how far the component should be from the top, right, left, right, horizontal center, or vertical center of the container) to position them.
The Application contains three children, a Label, a Panel, and a Group. The Label and Group controls are positioned within the Application container by setting x and y properties.
<s:Label x="20" y="40" text="XYZ Corporation Directory" .../> <s:Group includeIn="main" x="20" y="70"> ...
The Panel control is positioned within the Application container using constraints; it is positioned in the center of the application by setting its verticalCenter and horizontalCenter properties to 0.
<s:Panel id="loginPanel" verticalCenter="0" horizontalCenter="0" width="300" ...>
The Panel container is typically used to wrap self-contained application modules and includes a title bar, a border, a content area for its children, and an optional footer (called the control bar). You set the layout algorithm for the Panel by setting its layout property to an instance of a Layout class. In this sample, the VerticalLayout class is used so the children inside of it (the Labels and the VGroup) will be laid out vertically. If the children had any x, y, or constraint properties set, they would be ignored. The layout algorithm is customized by setting properties for that layout instance. The paddingTop of 30 specifies that there will be a 30 pixel space between the first child (the Label) and the top of the container (the Panel). A paddingLeft of 30 sets a 30pixel space between all children and the left edge of the container and a gap of 20 sets a 20 pixel space between children.
<s:Panel id="loginPanel" width="300" ...> <s:layout> <s:VerticalLayout paddingTop="30" paddingLeft="30" paddingRight="30" paddingBottom="30" gap="20" </s:layout> <s:Label id="errorLbl" .../> <s:Label .../> <s:VGroup id="loginForm"> <s:HGroup verticalAlign="middle"> <s:Label text="Username:" width="70" textAlign="right"/> <s:Label text="*" color="red" fontSize="20" verticalAlign="bottom"/> <s:TextInput id="username"/> </s:HGroup> <s:HGroup verticalAlign="middle"> <s:Label text="Password:" width="70" textAlign="right"/> <s:Label text="*" color="red" fontSize="20" verticalAlign="bottom"/> <s:TextInput id="password"/> </s:HGroup> <s:HGroup id="confirmFormItem" includeIn="register" verticalAlign="middle"> <s:Label text="Confirm:" width="70" textAlign="right"/> <s:Label text="*" color="red" fontSize="20" verticalAlign="bottom"/> <s:TextInput id="password2"/> </s:HGroup> </s:VGroup> <s:controlBarContent> <mx:LinkButton id="registerLink" label="Need to Register" .../> <mx:LinkButton id="loginLink" label="Return to Login" .../> <mx:Spacer width="100%"/> <s:Button id="loginButton" label="Login" .../> <s:Button id="registerButton" label="Register" .../> </s:controlBarContent> </s:Panel>
The form inside the Panel is created using a VGroup and HGroups. Children inside a VGroup are positioned vertically and those in an HGroup, horizontally. Each HGroup contains a "form element" consisting of a Label, a Label with an asterisk, and a TextInput control. Various styles and properties are set to make everything line up nicely. There actually is a new Spark Form container to help you more easily lay out forms, but it is not used in this application so that the more basic layout containers can be shown and explained.
To include children in the Panel footer, you use the controlBarContent property. By default, the controls in the control bar area use HorizontalLayout. (You can use the Panel controlBarLayout property to specify a different layout.) In the control bar in the application, a Spacer component (which is invisible) with a width of 100% is used between the buttons to make one button appear all the way to the left and one all the way to the right. It looks like there are four buttons, but only two will be displayed at a time. This is covered in the view states section later. If there was no spacer, the buttons would be laid out right next to each other against the left edge of the Panel. Component sizes are discussed more in the next section.
The last child in the Application tag is a Group container. The default layout for a Group is BasicLayout so in this case, the definition of the layout property is redundant but has been included for clarity. The Label has no x, y, or constraint properties set, so it will appear in the upper-left corner of its parent, the Group component, at 0,0. The DataGrid will be displayed 60 pixels from the top of the Group, which in turn is 70 pixels from the top of the main Application container.
<s:Group includeIn="main" x="20" y="70"> <s:layout> <s:BasicLayout/> </s:layout> <s:Label .../> <s:DataGrid top="60".../> </s:Group>
Component sizes
Up to now, the sizes of the components we've looked at were set in one of two ways. Either the size was set explicitly in pixels, like for the Panel:
<s:Panel id="loginPanel" width="300" ...>
... or no height or width properties were set at all and a default size for the component was used as was the case for the Label:
<s:Label x="20" y="40" text="XYZ Corporation Directory" .../>
For most components, the default size is "as big as it needs to be to hold its contents". For example, the Label is as big as it needs to be to display the text specified in its text property.
A third way to set size is to use percentages. After space is allocated for all the component's whose sizes are set using default or explicit sizing, the remaining space is divided up amongst the components asking for a percentage of it. In the control bar, the buttons used default sizing so they were as big as they needed to be to display their labels and the spacer took 100% of the remaining space. The result is that one button appears on the left edge of the control bar and the other on the right edge.
<mx:Spacer width="100%"/>
If the percentages asked for by the components in a container add up to over 100%, relative percentages are used. For example, if a container has three controls that all have their width set to 100%, each control will be allocated one third (100/300) of the width of the container.
View states
To provide more than one application "page" or view in your interface, you use view states, which provide a way to change the user interface dynamically at runtime—for instance to add, remove, move, or modify components. For every Flex view or component, you can define multiple states and then for every object in that view, you can define what state(s) it should be included in and what it should look like and how it should behave in that state.
To create view states, you set the states property equal to an array of State instances, assigning the name property of each. Four states have been defined for this application interface.
<s:states> <s:State name="login"/> <s:State name="register"/> <s:State name="main"/> <s:State name="loginerror"/> </s:states>
You use the includeIn or excludeFrom properties of a component to specify which states it should be included in or excluded from. If neither property is set, the component is inlcuded in all states. The XYZ Label appears in all states; the Panel is included in every state but main; the first LinkButton is included only in the loginerror and login states.
<s:Label x="20" y="40" text="XYZ Corporation Directory" .../> <s:Panel id="loginPanel" excludeFrom="main" ...> <mx:LinkButton id="registerLink" includeIn="loginerror,login" .../>
You can specify different properties, styles, and event handlers for each state by appending each with the name of the state to which it should apply. The Panel has a different title in the three states it appears in.
<s:Panel id="loginPanel" excludeFrom="main" title.login="Login" title.register="Register" title.loginerror="Login" ...>
You switch between states by setting the component's currentState property to the name of one of the defined states. When you click the register button, the application switches to the register state.
<mx:LinkButton id="registerLink" label="Need to Register?" click="currentState='register'" includeIn="loginerror,login"/>
Inline event handling
A typical Flex application consists of MXML code to define the user interface and ActionScript code for the logic. Just as for JavaScript and the browser DOM objects, the two are wired together using events and event handlers. You need to register to listen for some component event to occur and specify the ActionScript code to execute when it does. You can find all the events for a component listed in its API (application programming interface) in ActionScript 3 Reference for the Adobe Flash Platform.
On way to do make something happen as you just saw, is to simply specify both the event you want to listen for and the code to execute when it occurs right inline in an MXML tag. When the registerLink LinkButton is clicked, the ActionScript line of code switching to the register state is executed.
<mx:LinkButton id="registerLink" label="Need to Register?" click="currentState='register'" .../>
Be aware that ActionScript is case-sensitive so the case used for the currentState and register state name properties must match that in their definitions.
You could specify additional lines of code to execute by separating them with semi-colons, but this gets unreadable very quickly (and the code is not reusable). Instead, you specify a function to call when that event occurs. This is what you see for the loginBtn and registerBtn buttons.
<s:Button id="loginBtn" label="Login" includeIn="loginerror,login" click="loginBtn_clickHandler(event)"/> <s:Button id="registerBtn" label="Register" includeIn="register" click="registerBtn_clickHandler(event)"/>
ActionScript variables and functions
The event handler, the function to invoke when this event occurs is defined inside a Script tag.
<fx:Script> <![CDATA[ ]]> </fx:Script>
Remember that when you compile an application, the MXML is parsed and converted to ActionScript in memory and then the ActionScript is converted into a SWF that can be rendered by Flash Player. The CDATA tag says don't parse what's inside me; it does not need to be parsed and converted to ActionScript.
The <fx:Script> tag can go anywhere inside the MXML file. Order does not matter, but a common convention is to place it as the first inside the root tag of the MXML file.
Inside the Script block, you can put property (variable) declarations and method (function) definitions. This application, this MXML Application tag and its children, is an instance of the Application ActionScript class and for a class, you can define properties and methods. When objects were added with MXML, like the registerBtn Button, a property of the class is being defined (called registerBtn of type Button) and the object is added to the container so it appears on the Flash Player drawing surface. To define a property (variable) in ActionScript, you use the var keyword and to define a method (function), you use the function keyword.
[Bindable] protected var user:User=new User(); protected function loginBtn_clickHandler(event:MouseEvent):void{ //code to execute }
For each class member, you specify an access modifier (specifying what code can access it) and a data type (specifying what type of object it is or what type it returns). The four values for the access modifier are private (only code in this class can access the variable), public (any code in any class can access this variable), protected (only code in this class or subclasses can access this variable), or internal (only code in classes in the same package/folder can access this variable).
You specify data types using post colon syntax. Strong data typing is actually optional, but its use is strongly encouraged as it provides compile time code-hinting and errors as well as faster runtime performance. The user variable is of type User (whose class definition we'll look at shortly) and the two functions loginBtn_clickHandler() and registerBtn_clickHandler() return nothing, so their return data types are set to void. Required and optional function arguments are placed inside the parentheses () and the function body is placed inside the french (or curly) braces {}. Both functions in the code have one required argument of type MouseEvent.
Event objects
Every time an event is broadcast, it passes to the event handler an event object that has information about the type of event that occurred. For example, when you click a button, the event object will contain information about what button was clicked and where it was clicked (they x and y positions). When you change the selection in a drop-down list, the event object will have information about which list was changed and also have references to what item is currently selected. Because the type of information that is relevant for an event depends on what the event is, there are many different types of event classes defined. For example, when the click event of a Button occurs, an instance of the class is broadcast. When the change event of a List control changes, an instance of the is broadcast. All of the event classes extend the base event class, You can see what type of event object is broadcast with each event in a component's API in ActionScript 3 Reference for the Adobe Flash Platform.
Look at the loginBtn_clickHandler() and registerBtn_clickHandler() functions and see that they both receive an event argument of type MouseEvent. The event object is not actually referenced or used inside these particular functions, but it could be.
protected function loginBtn_clickHandler(event:MouseEvent):void{...} protected function registerBtn_clickHandler(event:MouseEvent):void{...}
To register to listen for an event of a component with ActionScript, you use the component's addEventListener() function. For example, in some function you would have the following code:
... where the second argument is a reference to the event handler to invoke when the click event occurs. Notice that the function is not being called (there are no parentheses after it), it is just being specifed as the function to invoke when the event does occur. You have no control over what gets passed to the function; it automatically gets passed an event object of whatever type was broadcast. With that in mind, let's go back and take a look at the inline MXML code for registering to listen for the button click events.
<s:Button id="loginBtn" label="Login" includeIn="loginerror,login" click="loginBtn_clickHandler(event)"/>
In this declarative MXML shortcut way of listening for an event, you are actually writing the ActionScript code to be executed inside an automatically generated listener function for the click event. If you want the event object that is passed to the automatically generated listener function passed to your function as well, you can pass an object called event which is the name assigned to the argument for the automatically generated listener function. This is what you see in the code above. In this case, because the event object is not actually used inside the function, the code could also have been written as:
<s:Button id="loginBtn" label="Login" includeIn="loginerror,login" click="loginBtn_clickHandler()"/>
... and:
protected function loginBtn_clickHandler():void{...}
The code generation features in Flash Builder always pass the event object though, because it a best practice to always write your event handlers with the required event object argument.
Next, let's look in more detail at the user variable which is being manipulated inside the event handler functions.
ActionScript classes
The user variable is defined as type User.
[Bindable] protected var user:User=new User();
Typically, you define one class per ActionScript file. The name of the class must match the name of the file and the location of the file must match the location specified for the class package. In this project, the User class in the valueObjects package is defined in User.mxml in the valueObjects folder.
package valueObjects { [Bindable] public class User{ public var name:String; public var username:String; public var password:String; public var email:String; public var deptid:uint; public function User(){ } } }
ActionScript 3 is an inheritance-based object-oriented scripting language very similar to Java but with some different syntax. In ActionScript 3, the class definition is included inside the curly braces of the package definition and the class members are defined inside the curly braces of the class definition. The User class has five public properties defined of type String and uint. (You can also create implicit getters and setters using get and set keywords.) You can find the main data types listed as the classes for the Top Level package in ActionScript 3 Reference for the Adobe Flash Platform.
The constructor, the function that is automatically called when an instance of the class is created, is defined as a function with the same name (and case) as the class and no return type. The User constructor here is empty and does nothing. (You can also leave it out of the code entirely and one will be automatically created.) You can specify required and/or optional arguments for the constructor function but you cannot overload functions in ActionScript 3.
If you return to the main MXML file and look at the Script block again, you will see the user variable declared as type User and set equal to a new instance of the User class.
import valueObjects.User; [Bindable] protected var user:User=new User();
The import statement is required so that the compiler can locate the definition for the User class. Import statements are written for you automatically in Flash Builder when you select a class from code-hinting.
The code inside the loginBtn_clickHandler() and registerBtn_clickHandler() functions should now make sense. Inside loginBtn_clickHandler(), conditional logic is used to check if a valid username and password combination was entered and if so, to populate the user object's name property and switch to the main application state. In registerBtn_clickHandler(),the user object's properties are populated with the values entered in the form. In a complete application, the code inside these functions would make calls to the server to either perform the user authentication check or to add a new user. Calls to the server are illustrated and discussed in the next sample project.
Next, let's talk about that [Bindable] tag in front of the user variable and inside the User class definition.
Data binding
Data binding is a powerful part of the Flex framework that lets you update the user interface when data changes without you having to explicitly register and write the event listeners to do this. The [Bindable] tag is a compiler directive; when the application is compiled, ActionScript code is automatically generated so that an event is broadcast whenever the variable whose definition it prefaces changes. For the user variable, an event is broadcast whenever it gets assigned a new value, for example when it goes from null to having a value or when it gets set to another instance of the User class.
[Bindable] private var user:User=new User();
The user variable is, however, a complex object and if you want an event to be broadcast when one of its properties change (not just when the variable in its entirety changes)—which you usually do—you need to specify this in the class definition. This is the purpose of the [Bindable] tag in front of the class definition in
[Bindable] public class User{
Now whenever a value of one of the publicly accessible properties for an instance of the User class changes, an event is broadcast.
You use curly braces around a value of an MXML attribute to register to listen for these changes and have the component display updated when that change occurs. If you look at the Label in the Group near the end of the code, you'll see curly braces as part of the expression to set the Label's text property.
<s:Label x="20" y="70" text="Welcome {}"/>
When the application is compiled, code is generated to listen for changes to and when its value changes, the Label display is updated accordingly.
Effects and transitions
Finally, let's finish up by looking at the transition code. By default, when a component changes state, all the changes (objects moving, appearing, disappearing, and resizing) happen at about the same time which can be an abrupt experience for the user. In order to make the transition smoother, you can animate it by changing properties in a gradual manner and/or by specifing the order in which the changes should occur. The Flex framework includes a number of predefined effect classes (located in the spark.effects package) including Fade, Rotate, Resize, and others. You can apply an animation to an object or to a view state change. When an animation is applied to a state change, it is called a transition.
To define transitions, you set the transitions property of a component equal to an array of Transition instances. For each Transition instance, you can set fromState and toState properties. The default values for these is *, the wildcard, which means for any state. Inside each Transition tag, you specify the animation to occur for that state transition. If you want more than one effect to occur, you can nest them inside Parallel or Sequence tags.
In the application, two transitions have been defined. The first will take place when changing from any state to the loginerror state; the second will occur for all other state changes. For the first transition, a sequence of actions occur: the Panel gradually changes size and then the errorLbl Label is wiped in to the right. For the second sequence, several actions happen in parallel over 1 second (1000 milliseconds): the Panel changes size and the confirmFormItem group fades in or out.
<s:transitions> <s:Transition fromState="*" toState="loginerror"> <s:Sequence> <s:Resize target="{loginPanel}"/> <s:AddAction target="{errorLbl}"/> <s:Wipe direction="right" target="{errorLbl}"/> </s:Sequence> </s:Transition> <s:Transition> <s:Parallel duration="1000"> <s:Resize target="{loginPanel}"/> <s:Fade target="{confirmFormItem}"/> </s:Parallel> </s:Transition> </s:transitions>
For effects which change the value of a property over time, as Fade changes alpha and Resize changes height and width, the start and end values for the effect correspond to the start and end values for the component in the state it is moving from and to.
The last thing to notice in the code is the use of {} when specifying the values for the target properties. The target property is typed as an Object and you set it equal to the visual object you want to animate or manipulate, not a string equal to the object's name. You need the {} to assign it a reference to the object instead of a literal string.