New Fireworks CS4-Flash Catalyst Integration

fw_logo_125x125If you’re a Fireworks user you’ll be happy to know that the team has been hard at work improving the FXG export feature so you can more easily take your designs from Fireworks and use them in Flash Catalyst. The new script is available over on Adobe Labs and includes a ton of fixes to the old FXG script as well as some new functionality based on a more finalized FXG specification:

  • lineHeight for Text element was always exported as %, now it’s exported based on a value selected in Fireworks ( % or exact)
  • Tab indention was not correct for elements that were exported as bitmaps
  • Exporting invisible bitmap elements caused script errors
  • Updated Rectangle primitive object to export transformation matrix and roundness value
  • Modified / Added Application Private Data for all elements (d:userLabel , d:type, etc.)
  • Exporting effects applied on Groups and Symbols

So fear not Fireworks users, Flash Catalyst may not have native file format import for you, but you’ll still be able to use the tool you know and love and bring those designs easily into Flash Catalyst with the “Open from FXG” feature.

A Week of Flash Collaboration: Shared Models and Collection Nodes

Update: This blog post took me the better part of a day and a half and just as I was posting it I realized I had a huge hole in the logic for my application because of the problem at the bottom. My plan is to come back and clean this up, as Peter suggested, divide it up into a couple of parts. The most important parts are the first couple of paragraphs that talk about the sharedModel classes and the last paragraph that mentions how the AFCS team architected things to build on top of very low level base classes like CollectionNode. Largely this blog post is a mess and digging into CollectionNode was interesting but I recommend it only after having some time playing with the other classes in sharedModel. After that this might be a useful post to read. Diving into the video tutorials is also highly recommended.

The basic building blocks of Adobe Flash Collaboration Service (AFCS) are really the sharedModel package and the CollectionNode classes included in it. Using the classes in the sharedModel package you can share any piece of data across AFCS connections and use these classes to build custom collaboration components like charts or maps where data can be updated and must change for everyone who is connected. I’ll first talk about some of the classes in the sharedModel package and then dive into how to use the CollectionNode, which provides the base functionality for sharing data.

Shared Models
The classes in the Shared Model package include a number of ways to share data and actions with AFCS. For the most part, the classes in sharedModel all use the CollectionNode to exchange data but they abstract a bit of the nuts and bolts of CollectionNode to make it easier to use. For instance the SharedProperty class lets you take any arbitrary property and share it across AFCS instances. It includes the ability to specify a CollectionNode or to just use one that will be generated as soon as the component using SharedProperty is created. Nigel Pegg has a great tutorial that covers using SharedProperty. Another set of classes in the sharedModel package are the Baton classes. The Baton is essentially a SharedProperty that gives you more control over when a user can edit something. The Baton class helps define when something is currently in use and shouldn’t be editable by anyone else in the room. It includes methods to grab() and putDown() a baton as well as a batonHolderChange event, which can be used to make changes to the user interface. Nigel’s video goes through this as well using the example of a TextInput box. Using the Baton classes it is easy to disable editing of the TextInput while someone else is typing inside of it.

CollectionNode All of the classes above use collection nodes and there are three major parts to the basic CollectionNode: creation, configuration, usage. As the documentation states, an AFCS room is essentially a bunch of CollectionNodes exchanging data and each of those nodes has specific configurations that define who and how people can interact with them. One thing to note is that CollectionNodes can only be created by a room owner, or someone with UserRoles.OWNER so when creating a CollectionNode, the owner must be the first person in the room so they can “initialize” it. After that the developer can use the NodeConfiguration classes to customize what level of user has read or write access to the node.

Creating a new CollectionNode is pretty straightforward. In this example I’m going to create a mini-game where the participants have to match up a ColorPicker to “win”. To set up the game, I’ve got one CollectionNode with a “Color” node that will contain the values of the ColorPicker. Here’s how I create the CollectionNode:

protected function application1_creationCompleteHandler(event:FlexEvent):void
{
     nodeGame = new CollectionNode();
     nodeGame.sharedID = "gameNodes";
     nodeGame.addEventListener(CollectionNodeEvent.SYNCHRONIZATION_CHANGE, onSync);
     nodeGame.addEventListener(CollectionNodeEvent.ITEM_RECEIVE, onItemRecieve);
     nodeGame.subscribe();
}

A few things to notice. First, the sharedID property is described in the docs as the “logical address of the collection within the room” and is how AFCS will refer to it so it needs to be unique. It is also the name that you will see when you browse the nodes in the room console that comes with the SDK. Second, in order to use the node we have to make sure it’s synchronized or we’ll get an error. After creating the node I added a CollectionNodeEvent listener that fires when we have a synchronization change event. I also added an event handler for when we receive messages upon subscribing to the node, which I’ll talk about later. Once the SYNCHRONIZATION_CHANGE event fires we can start making changes to our CollectionNode:

protected function onSync(event:CollectionNodeEvent):void
{
     var config:NodeConfiguration = new NodeConfiguration();
     config.modifyAnyItem = false;
     config.userDependentItems = true;
 
     nodeGame.createNode("Color",config);
     createMessage(false);
}

Now that we’re able to make changes to the CollectionNode I can create the Node that will store messages, in this case the “Color” Node. First I need to use the NodeConfiguration class to set up some rules for the game. By setting the modifyAnyItem property to false I make sure we’re not allowing users to modify messages that aren’t created by them. The userDependentItems property set to true means that when the user leaves the room, we clear out all of the messages belonging to them. In our game we don’t want to find a match with someone who isn’t playing any more. The NodeConfiguration classes allow us to tweak things like what happens to messages as well as define permissions for specific nodes within my CollectionNode. Once we’ve created the NodeConfiguration I can use the createNode() method to create a Node named “Color” and pass in the configuration values I just created. The last method there is a function I created. After we create the node and configure it I want to send the value of my ColorPicker to AFCS so I can start playing the game. I do that with a createMessage()

protected function createMessage(overwrite:Boolean):void
{
     var colorMessage:MessageItem = new MessageItem("Color");
     colorMessage.itemID = cSession.userManager.myUserID + "_color";
     colorMessage.body = color1.selectedColor;
 
     nodeGame.publishItem(colorMessage,overwrite);
}

My createMessage() takes one variable, a Boolean that we'll use to tell AFCS whether or not we are allowed to overwrite an existing message. The first part of the code is where we create our MessageItem to be sent. I'm just creating a new MessageItem and passing in which Node it will belong to, in this case the "Color" node I created above. Then I give it an itemID, which is how it will be referred to by AFCS. I use the current userID and prepend that to the word color. Finally I set the body of the message. The body of a MessageItem can be a String, a Boolean, a Number, or a key-value pair. In this case we're just going to pass in our selectedColor value. Once we create the MessageItem we need to add it to the Node. To do that we use the publishItem() method on the CollectionNode. This is where I pass in the overwrite flag. In this case, I don't want the MessageItem to be overwritten because this should be the first time we're adding it since we called it from the OnSync method. But later I want to make sure we overwrite it because we're making updates to it.

As soon as we publish our message, it's going to go into AFCS and then AFCS is going to send it back to us so we make sure it was sent correctly. When that happens it will fire an ITEM_RECEIVE event, for which we added an event handler in our initial function. That code looks like this:

protected function onItemRecieve(event:CollectionNodeEvent):void
{
     var tempMessage:MessageItem = event.item;
     var userName:String = cSession.userManager.getUserDescriptor(tempMessage.associatedUserID).displayName
 
     arrColors.push({color:tempMessage.body,user:userName});
     if(tempMessage.associatedUserID != cSession.userManager.myUserID)
     {
          if( tempMessage.body == color1.selectedColor )
          {
               Alert.show('Winner! Your partner is: ' + userName);
          }
     }
}

This is where we start to get to the game. The first two lines just put the MessageItem and user displayName into their own variables. Next we put the selectedColor from our message and the displayName into an array that's stored on the client. This is kind of important. A lot of the sharedModel classes, like SharedProperty for instance, will handle storing data for you but the CollectionNode class doesn't, it's a very basic, low level component. So in this case we can't tell AFCS to send us a list of messages to check and see if there is a match, we have to keep a record within our own application. I do that by storing the information in an Array. Once I do that I want to check if we have a match but I need to make sure we're not matching our own color so I do a quick check using the associatedUserID and myUserID from the ConnectSession. Once we know that isn't the case I can check the body of the message with my current color to see if there is a match and pop up the appropriate dialogue.

When we make changes to our color to try and find a match we also need to send the update to AFCS and check our Array so I added an event handler to my ColorPicker for any change event:

protected function color1_changeHandler(event:ColorPickerEvent):void
{
     createMessage(true);
     for(var i:Number=0; i<arrColors.length; i++)
     {
          if(event.color == arrColors[i].color)
          {
               Alert.show('Winner! Your partner is: ' + arrColors[i].user);
          }
     }
}

In this case the first thing we do is call createMessage() with the overwrite flag set to true so we are overwriting our current message with the new color value instead of creating a new one. Then we run through our array of colors to see if this new color matches anything on the system. Do you notice a problem?

The problem is this: We configured our Node to remove all messages as soon as someone leaves the room but because we're using CollectionNode instead of one of the higher level classes we also have that array that is storing the values. We're using that array to check for a match after we change our color but we don't ever clear out old messages from our array so we might get a match from someone who is no longer logged in. We're also not checking the array and updating values so we have a lot of old data.

Welcome to Multi-User Applications
Multi-user applications are fun but there is also a lot to deal with. Luckily, the AFCS team has done a very good job of building base functionality into classes like CollectionNode and then building on top of them with classes like SharedProperty that take care of a lot of the complicated parts of managing a collaborative application. I tried to get nitty gritty and show how CollectionNode works but I really encourage you to start with the higher level classes, get an idea of what's happening, and then break down to the low level base classes when you need to start creating very customized collaborative applications with specific rules, custom components, and many different types of data.

You can grab the full project here (Flash Builder 4 FPX file) and see how the parts fit together. I'll be updating it in the next few days to take into account the problem above and include a version that people can play.

A Week of Flash Collaboration: Authentication and Drupal Integration

afcs_logoThis is my first attempt at a series I’m hoping to start called “A Week of”, which will cover 5 topics in a particular subject over the course of the week. For the first one, I’m going to do Adobe Flash Collaboration Service and I’ll be doing a blog post on something AFCS-related every day. If you’re not familiar with Adobe Flash Collaboration Service (AFCS) I encourage you to check out my tutorial on the Developer Center to get started. Note: There is a new SDK version (0.93) on the Developer Portal, which includes everything in one place including video tutorials and an AS3-only version of the SDK. Worth checking out.

Authentication During Development
During development it’s relatively easy to authenticate against the Flash Collaboration service even if you aren’t online. The basic component for Authentication is the AdobeHSAuthenticator. During development the AdobeHSAuthenticator tag lets you pass in your Adobe ID credentials to connect to a room.

Adobe Flash Collaboration Service also lets you test and develop without having to connect to the AFCS servers with the LocalAuthenticator tag. There is an AIR application as part of the SDK that, when running, will let you use the LocalAuthenticator to pass in an arbitrary username and connect to a room on your local machine.

Guest Authentication
You can set up AFCS to just allow guests to log into your rooms without any authentication. There are a couple of things you need to do to set this up. First, you have to make sure the room is active. You can do this using the AFCSDevConsole AIR application that comes with the SDK. It’s also a good idea to make sure your room settings autoPromote guests so that when the anonymous users log in they are given publish access and can actually collaborate with other users. This can be done either in code with the RoomSettings tag or inside of the AFCSDevConsole.

afcs_dev_console

External Authentication
The most common use case is integrating your own authentication system with Adobe Flash Collaboration Service. AFCS uses a token system that can be generated using the Account Shared Secret, which is available when you create an account at the AFCS Developer Portal. The SDK comes with code for a number of server side languages including Java, PHP, ColdFusion, Python, and Ruby that help facilitate the generation of the token. In my example I took some of the sample code and scripts and integrated it with a Drupal environment. As a CMS, Drupal has its own authentication system and user administration. I created a basic module that exposes an AFCS chat room if the user is logged into the Drupal system and uses the existing Drupal username in the chat-room so that the Drupal users can chat with each other in real time.

I started off by creating an Admin page for my AFCS module that exposes a set of parameters needed to generate the token. That includes the account, or name, you create when you first sign up for AFCS, the room for this particular application, your Adobe ID username and password, and finally the shared secret.

afcs_module_settings

Now that I have those variables I can generate a token and set up the session. Using the scripts provided in the SDK it’s easy in PHP to create a new AFCSAccount object, log in, and get the session token.

$host  = "http://connectnow.acrobat.com";
$accountURL = "{$host}/" . variable_get(afcs_account, 'test');
$roomURL = "{$accountURL}/" . variable_get(afcs_room, 'test');
 
 
$account = new AFCSAccount($accountURL);
$account->login(variable_get(afcs_user, ''), variable_get(afcs_pass, ''));
$session = $account->getSession(variable_get(afcs_room, ''));
$token = $session->getAuthenticationToken(variable_get(afcs_secret, ''),$user->name,$user->name,50);

The variable_get functions are Drupal-specific but the code should be easy to follow. We first build the full roomURL based on the account and the room name. Then we login with our username and password and finally create a session based on the room. the getAuthenticationToken method takes the Shared Secret and the name that will display to all of the other connected users. The last parameter is the role, which is a number from 0 to 100 and tells AFCS what the user can do. 100 is a full host and 50 allows users to publish content and collaborate.

Now that we have the token and the roomURL we need to somehow get that into the SWF file. The external authentication example that comes with the SDK and my Drupal example both use FlashVars, which are read by the Flex application and then used to authenticate and set up the session. When the application finishes loading I call the Application.application.parameters object to start the session.

[Bindable]
public var authToken:String;
 
[Bindable]
public var roomURL:String;
 
protected function application1_applicationCompleteHandler(event:FlexEvent):void
{
     roomURL = Application.application.parameters.roomURL;
     authToken = Application.application.parameters.authToken;  
     cSession.login();
}

From there on out it’s a typical Flash Collaboration Service application that uses our token.

<rtc:AdobeHSAuthenticator id="auth" authenticationKey="{authToken}" />
<rtc:ConnectSessionContainer id="cSession" roomURL="{roomURL}"
authenticator="{auth}" autoLogin="false" width="100%" height="500"]]
     <mx:HBox width="100%" height="100%"]]
          <rtc:SimpleChat id="chat" width="80%" height="100%" />
          <rtc:Roster id="roster" width="20%" height="100%" dataProvider="{cSession.userManager.userCollection}" />
     </mx:HBox>
</rtc:ConnectSessionContainer>

Hopefully that provides an example of external authentication. If you want, you can download the Drupal module that I created here. It’s still in the very basic stages and I’ll be adding to it down the road but it does show off how to use Drupal authentication in your Adobe Flash Collaboration Service applications.

On the East Coast? Come Get Some Flash Catalyst Info

If you’re on the East Coast and you’re interested in Flash Catalyst then August is your month. Next week I’m going to be at DelveNYC doing a deep dive on Flash Catalyst, which will be targeted at user interaction designers. My session is on the 6th of August from 8:30 to 9:30 and registration is only $595 for a bunch of great design sessions.

For those in the Washington, DC area I’ll also be at CFUnited doing a talk on Flash Catalyst that will cover all of the basics including design-develop workflow. I’ll also be doing a talk on using Flex 4 and ColdFusion 9 as part of coverage on ColdFusion 9 with my Adobe colleagues. At CFUnited you get a double-dose of Catalyst because Dee Sadler will be talking about round tripping and wireframing. It should be fun, so sign up!

Building Custom Components in Flex 4: SkinParts

I built my first Flex 4 custom component today, a basic sortable list component that extends List. The only modification I added was a button that you could click to resort your list in alphanumeric order. I mostly wanted to get a feel for building a custom component and get a sense for how to use SkinParts. Hopefully this will be useful to others getting their feet wet in Flex 4 custom components.

The concept of a SkinPart in Flex 4 is one that gives the developer and the designer a lot of power. By listing something as a SkinPart, it means that you can put that particular item anywhere in your custom component using a skin file. SkinParts are also just other components so you can get the built-in functionality as well as any skinning that can take place by using them. In my SortableList, I decided to use a basic Button as the header. Defining a SkinPart is very straightforward, here’s the code for my custom component with the SkinPart definition.

public class SortableList extends List
{
     public function SortableList()
     {
          super();
     }
 
     [SkinPart(required="true")]
     public var header:Button;
}

All you have to do is create a metadata tag with SkinPart and the required attribute set to true or false. Right below that define what the id if your skin part has to be and then what kind of component it is.

To show and use the SkinPart you need to add it to the skin file and make sure the component type and id match. In my case, I’ve got a component with an id of header.

<s:Button id="header" width="100" height="20" x="0" y="0" />

Presumably, you want your new SkinPart to do something and this is where I ran into trouble. At first inside of my SortableList constructor I added an event listener on my header component. The problem is that when the constructor is called it doesn’t create the SkinPart. That happens during a separate operation. The secret then is to override the partAdded function, which is a method in the SkinnableComponent class, the basis for every new spark component.

The partAdded function is automatically called and will add all of the SkinParts you specify in your custom component. It takes two parameters, the partName and the instance. Make sure to call super.partAdded so that your SkinPart gets added to the component and then you can use the instance variable to check when your SkinPart is being created. That lets you add the event listeners that will fire when a user interacts with your skin part. In my case I just added a simple click handler:

override protected function partAdded(partName:String, instance:Object):void
{
     super.partAdded(partName, instance);
 
     if( instance == header)
     {
          header.addEventListener(MouseEvent.CLICK,header_clickHandler);
     }
}

If you just have one SkinPart, there doesn’t seem to be any need for the if statement because it will only run once. I have it there just to be safe. You also need to add code so that if the SkinPart is removed you also remove the event listener. That’s done by overriding the partRemoved function.

override protected function partRemoved(partName:String, instance:Object) : void
{
     super.partRemoved(partName, instance);
 
     if( instance == header )
     {
          header.removeEventListener(MouseEvent.CLICK, header_clickHandler);
     }
}

And that’s pretty much all there is to it. This is just a very basic implementation but it should get you started. The design/development flexibility you get by using SkinParts in custom components makes it a great feature. When I finish with my SortableList component I’ll blog it and talk more about things I run into.

Flash Player Multi-Touch: Confirmation from Kevin Lynch

flash_multitouch

At the Adobe Analyst Summit today Kevin Lynch gave some clarity around Flash on mobile devices which included news that Flash Player for mobile will have support for multi-touch and accelerometer features. James Governor had the first tweet; which also included info about the release date, namely a public beta before the end of the year and release early next year, but if you’ve been following along you probably know that.

Having more native device capabilities on the phone via Flash is going to let Flash developers do some very interesting things. Think about the implication for games, video, augmented reality, and general application user interfaces. If you’re interested in the Flash Platform and multi-touch Daniel Dura is going to be doing a session at MAX covering some of the advancements in the Flash Platform and multi-touch. It should be an interesting session. Considering how creative Flash designers and developers are, I think this is going to be a feature that will get a lot of interesting use.

Image from Techdu.de

Two New Open Source Projects at Adobe

Today we’re announcing two more projects going up on opensource.adobe.com and becoming part of the open source family at Adobe. The first is the Text Layout Framework, which comes from some of the advancements we made in Flash Player 10 to improve text support in Flash Player. The other is the Open Source Media Framework, which was known by the codename “Strobe” and provides a robust framework for media playback of any kind (video, audio, dynamic SWFs).

The Text Layout Framework (TLF) is something that’s going to be a huge boon to developers. If you’ve been working with text in the new Flex 4 components then you’ve been working with the Text Layout Framework. If you haven’t seen the demo you can check it out over on Labs. It was created by a group that is just a few blocks north of me and does a great job of showing off the features of the new TLF. Now that the Text Layout Framework is open source you can push, pull, and extend it to your heart’s content. A great example of this in action is the New York Times Reader and the Boston Globe Reader – both of which wouldn’t have been possible without the Text Layout Framework.

The other project we’re releasing is the Open Source Media Framework (OSMF). I’ve been digging into the documentation a bit and I’m excited about what this means for rich media and the Flash Platform. The OSMF includes hooks for any kind of media type the Flash Player supports including images, audio, SWF content, and of course video. Using the framework you can create your own media players and the OSMF provides a set of powerful baseline functionality. It has hooks for creating your own plug-ins for metrics, advertising, and other functions. It has support for both progressive download and streaming built in as well as all of the video controls and functionality. And there isn’t any UI associated with the OSMF so you can integrate it into your application however you want.

I encourage you to download the source code and check out the samples. There are some good examples that show how to go about building plugins, how to use the composite media features (so you can support a number of different media types in one player), and how to build UI components on top of the framework.

Flash Distributable Player Now Available in 45 Countries

Last week Mark Doherty blogged about the fact that the Distributable Player is now available in a total of 45 countries. One of the nice things about the Distributable Player solution is that you don’t have to bundle a version of Flash with your application. You simply package it up using Adobe Mobile Packager 1.1 and it has all of the information about where to grab the runtimes inside of it so that when someone in one of the 45 countries below goes to install your application it will download Flash Lite if they don’t already have it installed. It’s similar to AIR’s model of making sure you’re only packaging and downloading what you need.

As Mark says, this is a great way to start building mobile applications today while you’re waiting for Flash Player 10. You can see the full list of supported devices and countries over on Labs. There’s also a great tutorial for getting started with the mobile packager that I’ve embedded below.

Distributable Player Supported Countries

Distributable Player Supported Countries

Are you Bored With Adobe AIR?

Sarah Perez over at ReadWriteWeb has a post up titled Are you Over AIR Applications in which she talks about her change in perspective on the value of AIR and how much benefit desktop applications provide over browser applications. It’s a pretty good post, and one that I hope drives some traffic and conversation, especially as we’re hearing more about things like Chrome, Firefox 3.5, and the Chrome OS.

For much of the past couple of years web applications were trying to mimic basic aspects like functionality and look and feel of desktop applications. That drove the movement towards RIAs and the shift made it painfully obvious that the browser in its current form wasn’t up to snuff. So more and more energy went into improving the browser so that web applications could compete against their entrenched desktop counterparts. We’re finally seeing releases from all that work. Firefox 3.5 looks to incorporate HTML5′s support for offline mode. Chrome was written from scratch because Google felt, basically, that the current browsers weren’t powerful enough to run complex HTML/Javascript based web applications. So what benefits does AIR have in this world? I agree that desktop applications as we know them are falling by the wayside, but AIR still has a few areas I think make it shine.

Web Technologies
One of the best parts of AIR is that it uses web technologies like HTML/Javascript and Flash. Web developers are a creative and innovative bunch. I’d argue that one of the main reasons Web 2.0 exploded the way it did was because web developers took to their destiny as the drivers of technology. Web development is relatively easy to learn but complex enough to keep the challenges coming. It’s also more productive than traditional languages because it’s both faster and it’s cross-platform. The strength of web development is a strength of AIR. Look at the first wave of a game-changing technology like Twitter. Almost all AIR applications. That’s because web technologies are easy and AIR made it very simple to quickly create a new kind of experience for a new kind of service. Tweetdeck and Twhirl got a first mover advantage and reaped the rewards. The development speed that the web allows for shouldn’t be discounted.

Notifications and Files
I think notifications, or the “toast” windows that you can pop up in AIR are more and more important as the web gets more real time. People want the firehose and they want it as soon as they can get it. Another area that I think AIR hasn’t been used enough for are filetypes. It’s incredibly powerful to be able to not only create items on the file system but to associate those with your applications. So far there hasn’t been need to create things like a .twitter file extension, but the next generation of web services may see big benefits from users being able to create those extensions. And of course with the file system you get some inherent benefits like the ability to tie into Spotlight or other desktop searches.

Ultimately I think both the browser and a more web-centric approach to desktop applications will succeed. The cross platform benefits, the improved developer productivity, and the close integration with web services are going to be instrumental in driving adoption for web applications both inside and outside of the browser. I hope AIR continues to do well and help drive innovation for web applications on the desktop. Seeing technologies like Google Gears and Titanium’s Appcelerator prove to me that the space is still growing and that we’ve got a lot of demand for a blend of web and desktop. And we’ve got a lot of enhancements coming up in the next version of AIR, so we’re not standing still. Stay tuned.

Fun with Flex 4 Skinning and the ToggleButton

I’ve been having a blast with the new Flex 4 skinning model and the flexibility/customization it provides. One of my favorite components to mess with is the ToggleButton because it’s so basic (either on or off) and yet pretty complex when you get down and dig in. It extends ToggleButtonBase which extends ButtonBase so it has the typical up/over/down/disabled states but because it’s a ToggleButton it also has the selected equivalent of all of those. That ends up making it difficult to do transitions between states because the transitions between the interim states like down to downAndSelected are so short. And if you roll off of it or move the mouse it fires an over state so the transition will look jerky.

Even with this limitation you can have some fun. When creating a transition between on and off you primarily want to deal with the over and overAndSelected states. Creating transitions between those two states will let you create something custom as the ToggleButton moves from the off state to the on state. And since the new Flex 4 component model lets you swap out skins at runtime it’s easy to create some interesting themes on the traditional ToggleButton. You can check out the SWF below (with source enabled) to see what I mean. And the code is very straight forward. The only variation is in the skin files:

<s:states>
	<s:State name="normal" />
	<s:State name="text" />
	<s:State name="beer" />
	<s:State name="coins" />
</s:states>
<mx:ComboBox id="cb" y="5" x="0" change="this.currentState = cb.selectedLabel;">
	<mx:dataProvider>
		<mx:ArrayCollection>
			<fx:Object id="normal" label="normal" skinClass="{spark.skins.default.ToggleButtonSkin}" />
			<fx:Object id="text" label="text" skinClass="{components.TextToggle}" />
			<fx:Object id="beer" label="beer" skinClass="{components.BeerToggle}" />
			<fx:Object id="coins" label="coins" skinClass="{components.CoinFlip}" />
		</mx:ArrayCollection>
	</mx:dataProvider>
</mx:ComboBox>
<s:ToggleButton id="toggle" y="100" x="0" skinClass="{cb.selectedItem.skinClass}" />

Some notes:

  • A selected beer is an empty beer.
  • Notice in the code above that skinClass is a bindable attribute. Makes it easy to swap in and out.
  • Be careful when you move the mouse after clicking the ToggleButton or some states won’t play correctly.
  • When clicking the coin, keep the mouse towards the top of the coin and don’t move it or you won’t get the effect.