Andy Hulstkamp

about creating online experiences

18. June 2009

Flex Spark Icon Gradient Button

Here’s an example of a Spark Icon Button done in Flex 4. There are smooth transitions on the background gradient between the up and over states. From the over state to the down state the shadows are removed smoothly. Each button has a different base color.

Flex Icon Gradient Button

Demo. View source is enabled.

Apart from the new skinning architecture there are a couple of aspects that make defining the look and feel of Spark Components easy:

  • Transitions and effects that look up the values in the states
  • Pseudo-Selectors for component-states, as well as id- and descendant selectors
  • Enhanced states with group states.
  • Colorization and exclusion

Transitions & effects

In this example the skin defines some transitions. There are AnimateColor effects that work on the gradient entries. There’s no special configuration involved, since the default-property that AnimateColor works on is… color . On the GradientEntry object there is a property for color. Flex automatically looks up the color values from the referenced states on the gradient entries.

In the states declarations there is an additional stateGroups ‘overStates‘ defined, that is referenced by the gradient entries. So there’s no need to explicitly define the colors for every state.

<s:states>
  <s:State name="up" />
  <s:State name="over" stateGroups="overStates"/>
  <s:State name="down" stateGroups="overStates" />
  <s:State name="disabled" />
</s:states>
.
.
.
<s:Transition  fromState="up" toState="over" autoReverse="true" >
  <s:AnimateColor  targets="{[ge1, ge2, ge3, ge4]}"  duration="250" />
</s:Transition>
<s:Transition  fromState="over" toState="up" autoReverse="true" >
  <s:AnimateColor targets="{[ge1, ge2, ge3, ge4]}" duration="750" />
</s:Transition>
.
.
.
<s:fill>
  <s:LinearGradient x="32.019" y="0.5" scaleX="25.5708" rotation="90">
    <s:GradientEntry id="ge1" color="#c0c0c0" color.overStates="#ffffff" ratio="0"/>
    <s:GradientEntry id="ge2" color="#939393" color.overStates="#e3e3e3" ratio="0.5"/>
    <s:GradientEntry id="ge3" color="#7e7e7e" color.overStates="#cecece" ratio="0.5"/>
    <s:GradientEntry id="ge4" color="#6a6a6a" color.overStates="#bbbbbb" ratio="1"/>
  </s:LinearGradient>
</s:fill>

There are also Animate effects that work on the strength of the DropShadowFilters. The SimpleMotionPath entries tell the Animate effect to work on the strength property. Note that there are no values defined on the effects & properties of the transitions. Flex automatically looks up the start and target values from the referenced states.

<s:Transition  fromState="over" toState="down" autoReverse="true" >
   <s:Animate duration="150" targets="{[dsfBg, dsfSymbol]}" >
    <s:SimpleMotionPath  property="strength" />
  </s:Animate>
</s:Transition>
<s:Transition  fromState="down" toState="*" autoReverse="true" >
  <s:Animate duration="150" targets="{[dsfBg, dsfSymbol]}" >
    <s:SimpleMotionPath property="strength"  />
  </s:Animate>
</s:Transition>
.
.
.
<s:filters>
  <s:DropShadowFilter id="dsfBg" alpha="0.5" blurX="0" blurY="0" distance="1" strength="1" strength.down="0"  />
</s:filters>
.
.
.
<s:filters>
  <s:DropShadowFilter id="dsfSymbol" blurX="0" blurY="0" distance="1"  strength="1" strength.down="0"/>
</s:filters>

Now, the buttons have a subtle effect when the mouse is rolled over and pressed: The gradient colors get lighter and the Shadows disappear giving the impression that the button is pressed.

Styles. Pseudo-, Type-, Id- & Class-Selectors

We defined the gradients in grayscale. What when we want colored buttons? No need to change the gradients. Flex automatically colors the components based on the baseColor style. We can set the baseColor for each state using pseudo selectors. Here’s an example of how to style the button that shows all of the selector types (id-, type- and class-selectors):

@namespace mx "library://ns.adobe.com/flex/halo";
@namespace s "library://ns.adobe.com/flex/spark";

/*   
 *    Using ID selectors with compund selectors (type and class selectors) 
 *    to define the appearance of the buttons 
 */

/* Define normal state styles on spark button type inside a desc container */
#desc s|Button {
    skinClass: ClassReference('AHSimpleIconButtonSkin');
    baseColor: "0x505050" ; /* Do not use black black; /*"0x56d9e5";*/
    color: #FFFFFF;
}

/* Define over states styles on spark button type inside a desc container */
#desc s|Button:over {
    baseColor: "0x97CD20";
}

/* Define down states styles on spark button type inside a desc container */
#desc s|Button:down {
    baseColor: "0xa0d828";
}

/* compound. override the over state of the button in element desc for class styleClass blueButton */
#desc .blueButton:over {
    baseColor: "0x56d9e5";
}

/* compound. override the down state of the button in element desc for class styleClass blueButton */
#desc .blueButton:down {
    baseColor: "0x56d9e5";
}

/* compound. override the over state of the button in element desc for class styleClass redButton */
#desc .redButton:over {
    baseColor: "0xd52A03";
}

/* compound. override the down state of the button in element desc for class styleClass redButton */
#desc .redButton:down {
    baseColor: "0xE53A13";
}
.
.
.
<s:HGroup id="desc" horizontalCenter="0" verticalCenter="0" verticalAlign="middle" gap="5">
    <s:Button  label="Green"     useHandCursor="true" buttonMode="true" />
    <s:Button  label="Blue"     useHandCursor="true" buttonMode="true" styleName="blueButton"/>
    <s:Button  label="Red"         useHandCursor="true" buttonMode="true" styleName="redButton"/>
</s:HGroup>

Colorization & Exclusion

By setting the baseColors for different states, Flex will colorize the component accordingly. A dark grey in the up-state, a color in the over state and a lighter color in the down state.

The bad thing is that everything gets colorized. The good thing is that you can overwrite a function on SparkSkin to exclude elements from being colorized. We only want the background gradient to get colorized, so lets exclude the other elements from colorization:

<fx:Script>
<![CDATA[         
    /* Define the skin elements that should not be colorized. 
    exclude symbol and text group */
    static private const exclusions:Array = ["symbol", "textGroup"];

    /** 
     * @copy spark.skins.SparkSkin#colorizeExclusions
     */        
    override public function get colorizeExclusions():Array {return exclusions;}
]]>        
</fx:Script>
.
.
.
<!-- The group with the symbol with a shadow applied to. In a group for colorization exclusion -->
<s:Group id="symbol" verticalCenter="0" left="7" top="9" right="7" bottom="7">
    <s:filters>
        <s:DropShadowFilter id="dsfSymbol" blurX="0" blurY="0" distance="1"  strength="1" strength.down="0"/>
    </s:filters>
    <s:Path winding="nonZero" data="M12.6924 0L5.76855 6.92383 2.30762 3.46191 0 5.76855 3.46191 9.23145 5.76855 11.5391 8.07617 9.23145 15 2.30762 12.6924 0Z" >
        <s:fill>
            <s:SolidColor color="#ffffff"/>
        </s:fill>
    </s:Path>
</s:Group>

There is a group with an id ’symbol’ that is referenced in the exclusion array. Do the same for the label element.

Source updated to Flex 4 release sdk.

further reading

Customize the Spark TextInput Component in Flex 4 (Gumbo). Adding focus and Transitions.

A custom Flex Spark Text-Input.

Custom Component using Flex SpriteVisualElement

A much more lightweight approach to create a simple custom Component in Flex Spark. Does not extend from the heavier SkinnableComponent but from SpriteVisualElement.