This article is sponsored by Adobe.
Working with States in Flex 4
by Frank Sommers
September 16, 2009

Summary
Flex 4's new syntax simplifies working with application states. This article provides a tutorial introduction into UI state management with Flex 4, and includes a complete example.
Advertisements

To view this page ensure that Adobe Flash Player version 10.0.0 or greater is installed.

Get Adobe Flash Player
Get Adobe Flash Player

Click on "Need to register?" to toggle between UI states. To view the source code, right-click or control-click on the application.

In a traditional, server-generated HTML user interface, the server may return an entirely newly rendered page when a user chooses to register instead of signing in. By contrast, a rich-client can affect the change in the user interface entirely on the client side, without interaction from the server.

The simplest way to code that is to write display logic that inserts and removes new labels and text boxes in the UI at runtime, for instance, by modifying the UI's DOM in the browser. More sophisticated rich-client frameworks, however, provide a much handier abstraction to accomplish the same goal: UI states.

UI States in Flex

A UI state is the set of all components and component properties in effect in a component at any given point in time. Multiple UI states can be associated with a component: Switching from one state to another causes the rich-client runtime to add, remove, or modify components based on the differences between states. The login form, for instance, could have "login" and "register" states: The register state defines additional input boxes, changes the label of the button component from "Sign in" to "Register", and also causes the button click to invoke a register() method instead of signin().

In Flex, defining and working with application states has been possible since the 2.0 version of the Flex SDK. In Flex versions 2 and 3, states were defined inside a states array in an MXML document; inside each state definition, in turn, were declarations of what should be added, removed, or modified, related to some base state. It was also possible to build one state on another.
<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml"> 

  <mx:Script>
      <![CDATA[
            private function signin():void {
                // Implementation of signin
            }

            private function register():void {
                // Implementation of register
            }
        ]]>
  </mx:Script>

  <mx:states>

    <mx:State name="Register">        

        <mx:AddChild relativeTo="{loginForm}" position="lastChild" creationPolicy="all">
            <mx:FormItem id="frmPasswordConfirm" label="Confirm:">
                <mx:TextInput id="passwordConfirm" displayAsPassword="true"/>
            </mx:FormItem>        
        </mx:AddChild>

        <mx:AddChild relativeTo="{loginForm}" position="lastChild">
            <mx:FormItem label="First name:" id="frmFirstName">
                <mx:TextInput id="firstName" width="220"/>
            </mx:FormItem>
        </mx:AddChild>

        <mx:AddChild relativeTo="{loginForm}" position="lastChild">            
            <mx:FormItem label="Last name:" id="frmLastName">
                <mx:TextInput id="lastName" width="220"/>
            </mx:FormItem>
        </mx:AddChild>

        <mx:AddChild relativeTo="{loginButton}" position="after">
            <mx:LinkButton label="Return to login" click="setCurrentUIState()"/>
        </mx:AddChild>            

        <mx:RemoveChild target="{registerLink}"/>            

        <mx:SetProperty target="{loginLabel}" name="text" value="Create a new account:"/>    
        <mx:SetProperty target="{loginButton}" name="enabled" value="false"/>
        <mx:SetProperty target="{loginButton}" name="label" value="Register"/>
            
     </mx:State>        

  </mx:states>
    
 <mx:Label text="Sign In:" 
            fontWeight="bold" paddingLeft="12" fontSize="14" 
            id="loginLabel" width="100%"/>
        
        <mx:Form id="loginForm" width="100%">

            <mx:FormItem label="Email:" id="frmEmail">
                <mx:TextInput id="email" width="220"/>
            </mx:FormItem>            

            <mx:FormItem label="Password:" id="frmPassword">
                <mx:TextInput id="password" displayAsPassword="true" width="220"/>
            </mx:FormItem>        
        </mx:Form>

        <mx:CheckBox id="rememberMe" label="Remember me on this computer"    
            paddingLeft="77" selected="true"/>

        <mx:ControlBar width="100%"    paddingLeft="84">
            <mx:Button label="Sign in" id="loginButton" click="signin()" enabled="false"/>            
            <mx:LinkButton label="Need to Register?" id="registerLink"
                click="currentState='Register'"/>            
        </mx:ControlBar>
</mx:VBox>

Listing 1: Login box with Flex 3

In the above Flex 3.0 implementation, AddChild and RemoveChild tags indicate that the runtime should add or remove child components, respectively, in the register state. The SetProperty tag, in turn, changes the button's label to "Register," and the SetEventHandler tag ensures that a register() method is invoked when the UI is the register state. Clicking on the LinkButton component toggles between the signin and register states.

While the Flex 2 and 3 state syntax works well, adding and removing components is rather cumbersome: For instance, you have to specify where new components are to be added apart from the new components' parents. UI components supporting a large number of states end up becoming unwieldy, with significant portions of related UI logic scattered across a large MXML file.

Flex 4's State Improvements

To simplify state definition, Flex 4 introduces a new states syntax. Although the various component states are still declared inside the states tag, the definition of each state is specified inline. To see how that works, consider the Flex 4 implementation of the login component. To make the Flex 4 implementation mirror the Flex 3 version as closely as possible, we didn't define separate component and skin files, a Flex 4 best practice:

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    xmlns:mx="library://ns.adobe.com/flex/halo">
        
    <fx:Script>
        <![CDATA[
            private function signin(): void {
                 // Implementation of signin 
            }

            private function register(): void {
                // Implementation of register
            }
        ]]>
    </fx:Script>
        
    <s:states>
        <s:State name="signin"/>
        <s:State name="register"/>
    </s:states>
        
    <s:Group id="mainGroup">
        <s:layout>
            <s:VerticalLayout/>
        </s:layout>
                
    <mx:Label text="Sign in:" fontWeight="bold" paddingLeft="12" fontSize="14" id="loginLabel" width="100%"/>
                
    <mx:Form id="loginForm" width="100%" paddingTop="3" paddingBottom="3">

        <mx:FormItem label="First name:" includeIn="register" id="firstNameItem" alpha="0.0">
            <s:TextInput id="firstName" width="220"/>
        </mx:FormItem>

        <mx:FormItem label="Last name:" includeIn="register" id="lastNameItem" alpha="0.0">
            <s:TextInput id="lastName" width="220"/>
        </mx:FormItem>

        <mx:FormItem label="Email:" id="emailItem">
            <s:TextInput id="email" width="220"/>                 
        </mx:FormItem>

        <mx:FormItem label="Password:" id="passwordItem">
            <s:TextInput id="password" displayAsPassword="true" width="220"/>
        </mx:FormItem>

        <mx:FormItem label="Confirm:" includeIn="register" id="confirmItem" alpha="0.0">
             <s:TextInput id="passwordConfirmation" displayAsPassword="true" width="220"/>                        
        </mx:FormItem>
    </mx:Form>

    <s:Group>
        <s:layout>
            <s:HorizontalLayout paddingLeft="100"/>
        </s:layout>
        <s:Button label="Sign in" label.register="Register" id="loginButton" 
            enabled="true" click.signin="signin()" click.register="register()"/>
                        
        <mx:LinkButton label="Need to register?" 
            click="currentState = 'register'" includeIn="signin"/>
        <mx:LinkButton label="Return to signin"
            click="currentState = 'signin'" includeIn="register"/>
     </s:Group>
                
    <mx:CheckBox id="rememberMe" label="Remember me on this computer" paddingLeft="110"/>
 </s:Group>
</s:Application>

Listing 2: Login box with Flex 4 state syntax

Instead of AddChild and RemoveChild methods, components that should be visible in the register state only are added an includeIn="register" tag. You can specify a comma-separated list of state names for the includeIn tag. Flex 4 also provides an excludeFrom tag, if you need to declare what states a component should be removed from. This new syntax allows you to declare components in place, inside the parent.

Properties can similarly be set inline using a new dot-separated syntax. For example, the Button component's label property is defined as follows:

... label="Sign in" label.register="Register" 

This tells Flex to set the button's label property to "Register" in the register state, and to "Sign in" in other states (the default). And listeners can be declared similarly: When the component is in the register state, the register() method is invoked on a button click, otherwise the signin() method is called:

click="signin()" click.register="register()"

The new component syntax makes it much easier to define sophisticated components with relatively straightforward, declarative code. The Flex 4 component states syntax also supports state groups as well as the ability to assign a new parent to a component. It is important to note that the new syntax only impacts MXML declarations—which is what the Flex compiler uses to generate ActionScript code. States can be defined in ActionScript as well, but that syntax does not change in Flex 4.

Flex gives you convenient ways not only to define component states, but also to specify transitions between states. To make the transition between the signin and register states smoother, this example includes a transition effect between those states. A forthcoming Artima article will describe Flex 4 transitions in greater detail.

Resources

Adobe's Flash Builder 4
http://labs.adobe.com/technologies/flashbuilder4

Flex 4 SDK
http://opensource.adobe.com/wiki/display/flexsdk/Flex+SDK

Gumbo Project
http://opensource.adobe.com/wiki/display/flexsdk/Gumbo

Flex.org
http://www.flex.org

Talk back!

Have an opinion? Readers have already posted 3 comments about this article. Why not add yours?

About the author

Frank Sommers is Editor-in-Chief of Artima.