Our Blog

There are a some new features available for skinning in Flex 3 beta 2. Some of them are covered by Juan and NJ. But I want to focus in one specific topic: Stateful Skins (without Flash).

These are very nice additions to the framework because they keep the code clean while giving more power when it comes to changing the behavior and look and feel of the component. With that ability, you could add transitions between one state to the other or change any property between states, without leaving Flex Builder.

Sexy Button
As an example, to create this button, I would have this code in my application:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" backgroundColor="0x3d3d3d" layout="absolute" width="400" height="200">
   <mx:Style source="assets/styles/Main.css"/>
   <mx:Button label="Sexy Flex Button" x="50" y="50" />
</mx:Application>

As you can see, I have a normal button and a link to a style sheet.

The style sheet contains the following code:

/* CSS file */
Button{
   skin: ClassReference("skins.ButtonSkin");
   fontSize:16;
   fontWeight:normal;
   paddingLeft:23;
   paddingRight:23;
   paddingTop:10;
   paddingBottom:10;
   color:#ffffff;
   textRollOverColor:#ffffff;
   cornerRadius:5;
   borderThickness:1;
   borderColor:#cccccc;
   borderStyle:solid;
   backgroundColor:#1e83b8;
}

So far, everything looks clean at this point: a button, and a style that references a skin ("skins.ButtonSkin"). A developer can work on the logic of the application without worrying about how the button looks. Later, a designer or a "designloper" can add transitions, colors and change the look and feel without breaking or modifying the application. A reference in the CSS to the skin property gives the designer the power to add a new class (ex: skins.ButtonSkin) where transitions, dropshadows and other effects are defined.

Usually (in Flex 2), we would create this skin as a programmatic skin and have some kind of logic to determine what to show depending whether this skin should be "up", "down", "over", etc, (if we used the same skin for the "upSkin", "downSkin", "overSkin", etc). Another easy way was to use an embedded asset as the skin. With these two approaches we would get one instance of the skin for each corresponding button state, making it difficult to have transitions between these skins.

With Flex 3, we can create only one class with states for each corresponding button state. I've implemented it as a MXML component, using a Canvas as the super class, because I'm lazy ;) but I could have used any UICoponent or any class that implements IStateClient.

In this canvas, I implemented a state for each skin in the button: down, over, up (I didn't add disabled because I don't use it) and I can create transitions between states. In this example, I made a color transition from blue to orange (thanks Darron for the library) and set other properties like adding filters.

Disclaimer, I'm changing the background color by calling setStyle. That is not a good practice because setStyle is very expensive. It is better to use other method to make the same effect.

The code of the ButtonSkin is the following:

<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas
   xmlns:mx="http://www.adobe.com/2006/mxml"
   xmlns:ds="com.darronschall.effects.*"
   creationComplete="init()">

   <mx:Script>
      <![CDATA[
         import mx.controls.Button;
         import mx.core.UITextField;
         
         use namespace mx_internal;
         
         [Bindable]
         private var textField:UITextField;
         
         private function init():void
         {
            var button:Button = (parent as Button);
            textField = button.getTextField();
            filters = [bevel, dropshadow];
         }
         
      ]]>
   </mx:Script>
   
   <!-- States .............................................. -->
   <mx:states>
      <mx:State name="up">
         <mx:SetProperty target="{textField}" name="filters" value="{[textShadow]}"/>
</mx:State>

<mx:State name="down">
      <mx:SetProperty target="{this}" name="filters" value="{[bevelDown]}"/>
      <mx:SetProperty target="{glow}" name="color" value="{0xf1c990}"/>
      <mx:SetProperty target="{textField}" name="filters" value="{[glow]}"/>
</mx:State>

<mx:State name="over">
   <mx:SetProperty target="{textField}" name="filters" value="{[glow]}"/>
   <mx:SetProperty target="{bevel}" name="highlightAlpha" value="0.8"/>
</mx:State>
</mx:states>


<!-- Filters .............................................. -->
   <mx:DropShadowFilter id="dropshadow" alpha="0.5" quality="3" blurX="6" blurY="6"/>
   <mx:BevelFilter id="bevel" angle="90" highlightColor="0xffffff"
      shadowAlpha="0" strength="3" quality="3" distance="24" highlightAlpha="0.4"/>

   <mx:BevelFilter id="bevelDown" type="inner" angle="245" shadowAlpha="0.4"
      highlightAlpha="0.4" distance="8" blurX="10" blurY="10" quality="4"/>

   <mx:DropShadowFilter id="textShadow" alpha="0.5" quality="2"
      distance="2" blurX="2" blurY="2" />

   <mx:GlowFilter id="glow" color="#b86113" alpha="0.8" />
   
   
   <!-- Transitions .............................................. -->
   <mx:transitions>
      <mx:Transition id="toOver" fromState="*" toState="over">
         <ds:AnimateColor target="{this}" property="backgroundColor"
            isStyle="true" toValue="0xff9600" duration="300" />

      </mx:Transition>
      <mx:Transition id="toUp" fromState="*" toState="up">
         <ds:AnimateColor target="{this}" property="backgroundColor"
            isStyle="true" toValue="0x1e83b8" duration="300" />

      </mx:Transition>
      <mx:Transition id="toDown" fromState="*" toState="down">
         <ds:AnimateColor target="{this}" property="backgroundColor"
            isStyle="true" toValue="0xff9600" duration="300" />

      </mx:Transition>
   </mx:transitions>
   
   <!-- Background Images .............................................. -->
   <mx:Image source="@Embed(source='assets/images/dec_left.png')"
      left="5" verticalCenter="0"/>

   <mx:Image source="@Embed(source='assets/images/dec_right.png')"
      right="5" verticalCenter="0"/>

</mx:Canvas>

This is just the beginning. Ely Greenfield showed what is coming in Flex 4 at MAX and they are making these things even easier. I'm very excited.

I also want to see how I can use Degrafa to implement some more complex skins.

If you want to read more, I recommend checking the docs

View example
Download the source

Nahuel Foronda

Nahuel Foronda

32 Comments

  1. Tink
    In theory we could do this pretty easy in the Flex 2 right?

    I means the skin just has a switch statement where the state is change immediately, but applying a tween would be as easy as handing beers out to drunks.
  2. Ben
    Nice post, I didn't realize we could do this in Flex 3. I was also in Ely's session at MAX and have been thinking about contacting the Degrafa guys to see if I could replicate Ely's approach using their library. Might make for an interesting side project...
  3. Juan
    Very cool. We were hoping to have a beta out of Degrafa pretty soon, but we all have day jobs too :) I'm wondering if we'll start seeing Graphics Tags and States/Transitions showing up in the framework as we get closer to Flex 4, maybe even the tail end of Flex 3.
  4. Narciso (nj) Jaramillo
    Very cool! This is a really good example of exactly what we were thinking when we made skins stateful. Thanks for posting it!

    nj
    Adobe Flex team
  5. Narciso (nj) Jaramillo
    One thought just occurred to me...Did you try using transitions instead of Darron Schall's AnimateColor? I bet that would work, but haven't tried it myself yet.

    nj
  6. Nahuel
    I just updated the example and added transitions as Narciso recommended. With the transitions the effect looks better, I don't need to call animateColor.play() on the event enterState and transitions also prevent jumping from one color to another when you move the mouse fast.
    The only thing that I couldn't do was to get rid of Darron Schall's classes because the colors are in hexadecimal and the only way that I found to have a nice transition is to use his effect. Maybe Narciso has a tip for that ;)
    @Tink: You can do that in Flex 2 but is not as simple as this and it would require more work.
  7. Josh Rodgers

    Josh Rodgers

    This is going to be extremely useful, thanks for the post!
  8. Tink
    "@Tink: You can do that in Flex 2 but is not as simple as this and it would require more work."

    Ah yeah I meant the prev version of Flex 3.0 really. Pretty pointless but I thought i'd have a crack http://www.tink.ws/blog/stateful-skins-in-flex/. Just needed to override updateDisplayList and add a switch statement.

    It becomes and pain in Flex 2.0 as you can't just supply 1 skin for all states.

    P
  9. Narciso (nj) Jaramillo
    I think Nahuel is right--you can't get rid of AnimateColor. I was thinking you could use AnimateProperty, but that probably doesn't work for styles. I'll mention it to the appropriate SDK folks to see if we could do something about this for Flex 4. Thanks!

    nj
    Adobe Flex team
  10. Narciso (nj) Jaramillo
    Oops--I was wrong about why AnimateProperty doesn't work--it does work for styles, but as you said it doesn't work for hex colors. My bad.
  11. paddy
    could you enlighten me as to why this statment is needed:

    use namespace mx_internal;

    p;)
  12. Nahuel
    Hi Paddy,
    I need that because button.getTextField(); is under that namespace.
    I'm adding bevel and dropshadow to the textfield of the button. The only way to access that textfield is using its namespace.
  13. Toni
    Hello!

    Nice example. How can I make a image transition in the button?
    I want the image to fade while the button changes image.
  14. paul
    really nice button. I'm new to Flex and am trying out your code as is...but I keep getting this error:
    -- 1118: Implicit coercion of a value with static type mx.core:IUITextField to a possibly unrelated type mx.core:UITextField.   ButtonExample/skins   ButtonSkin.mxml   line 19   1201723829300   46 --

    Can anyone tell me how to correct this error?
    Thanks!
  15. paul
    really nice button. I'm new to Flex and am trying out your code as is...but I keep getting this error:
    -- 1118: Implicit coercion of a value with static type mx.core:IUITextField to a possibly unrelated type mx.core:UITextField.   ButtonExample/skins   ButtonSkin.mxml   line 19   1201723829300   46 --

    Can anyone tell me how to correct this error?
    Thanks!
  16. Keith
    I am getting the same error, anyone have any ideas?
  17. Nahuel
    If you are having problems with the UITextField, you need to change this 2 lines

    import mx.core.UITextField;
    to
    import mx.core.IUITextField;

    and

    [Bindable]
    private var textField:UITextField;
    to
    [Bindable]
    private var textField:IUITextField;
  18. Jason The Saj
    "I'm changing the background color by calling setStyle. That is not a good practice because setStyle is very expensive. It is better to use other method to make the same effect."

    What is the other better method to use?

    ???

    Second, what about loading CSS dynamically, when using ClassReference?
  19. Jason The Saj
    Two Inquiries:

    1. Say I wanted to change the label value on a state change. How would I address the button's label?

    2. Any thoughts on incorporating Degrafa elements into a stateful skin?

    - The Saj
  20. Nahuel Foronda
    @ Jason,
    Regarding setStyle, I know that is an expensive method, but I did that because it was easy :)
    When I created this example, Degrafa was not available. I think that makes more sense to do the painting with Degrafa instead of calling set style.

    About your label value question, I recommend that you don't change the value in the skin, instead change it outside. You can listen to the events that the button dispatches to do it.

    Addressing your question 2, yes, incorporating Degrafa is the next step. As I mentioned, that was not available at that time.
  21. yalasta

    yalasta

    Hi, This is really nice to me, Im really new in flex, I tried this code is is. I got same error like Paul - 1118: Implicit coercion of a value with static type mx.core:IUITextField to a possibly unrelated type mx.core:UITextField. ButtonExample/skins ButtonSkin.mxml line 19 1201723829300 46 --. I changed this import mx.core.UITextField;
    to
    import mx.core.IUITextField;
    and
    [Bindable]
    private var textField:UITextField;
    to
    [Bindable]
    private var textField:IUITextField;

    after I got this error:

    Severity and Description   Path   Resource   Location   Creation Time   Id
    Could not resolve <ds:AnimateColor> to a component implementation.   SimpleButton/src/Components   ButtonSkin.mxml   line 60   1214337487515   185

    also at line 64 and 68. How to fix this problem?
    thanks a lot.
  22. Paul
    Too bad the code in your example is missing...
  23. Jeremy
    I would like to use your example in a ButtonBar ... but I can't seem to get the style to be used by the button bar buttons, even when specifying buttonStyleName="Button", which is the same style used by your original button ... any ideas ?
  24. pulse00

    pulse00

    Does anyone know how stateful skinning is done when implementing custom states ? the example doesn't seem to work when using non-button states.
  25. softlogic

    softlogic

    this skin does not work when button is disable, does anyone know how to make it usefull when the button is disabled.
  26. Dave
    Good article . I found a simple skinning tutorial

    at

    http://www.askmeflash.com/tutorial/4/skinning-a-flex-button-tutorial-using-skin-in-flex