Flex Mobile Development: Creating Dialog Windows

Ever wondered how to create a modal dialog window in Flex mobile applications? For example, you may want to ask the user for a confirmation when he performs a delete operation or maybe to select an item from a list. Well, if you don’t know how to do it, then read on.

Flex has a class that just does this: SkinnablePopUpContainer. SkinnablePopUpContainer extends SkinnableContainer class and has a very simple API you can use in order to “open” and “close” the dialog.

Understanding SkinnablePopUpContainer

Because SkinnablePopUpContainer extends the SkinnableContainer it is extremely easy to create any kind of dialog window you want. You can create a new MXML component that extends the SkinnablePopUpContainer. Then you set a layout manager that works for you (VerticalLayout, HorizontalLayout). And finally, you add the UI components you need – labels, buttons, lists, and so on.

Suppose you want to create a simple alert window that looks like this:

Here is the code to implement this (as a new MXML component that extends SkinnablePopUpContainer):

<?xml version="1.0" encoding="utf-8"?>
<s:SkinnablePopUpContainer xmlns:fx="http://ns.adobe.com/mxml/2009"
    xmlns:s="library://ns.adobe.com/flex/spark"
    backgroundColor="0x898989">

    <s:layout>
        <s:VerticalLayout horizontalAlign="center"/>
    </s:layout>

   <s:Label text="The contact was saved" width="100%" textAlign="center"/>
   <s:Button label="OK" width="100%" click="this.close()"/>
</s:SkinnablePopUpContainer>

The API for controlling the SkinnablePopUpContainer is pretty simple:

  • To open or display the window you create a new instance and then call the open() method of SkinnablePopUpContainer. This method has two arguments. The first argument is the display object container where it will be created. The second argument is a flag that tells Flex if the window will be modal or not.
  • To close the window and send data from the window to the application you use the close() method of SkinnablePopUpContainer. This methods also has two arguments. The first argument is a flag. You can set this to true if, for example, the user clicked the OK button, and false otherwise. If you want to send some data back to the application you use the second argument. For example, if you have a TextInput in your dialog and you want to send the text you will use this second argument.
  • To listen for the close event and read the data sent from the SkinnablePopUpContainer you have to register an event listener on the SkinnablePopUpContainer. The event type is PopUpEvent. PopUpEvent has two properties that you can use to read the flag and data set when calling the close() method: commit and data.

Here is an example of how to open a SkinnablePopUpContainer component and read the flag/data when it is closed:

//create the component and open it
var alertWindow:SkinnablePopUpContainer = new SkinnablePopUpContainer();
alertWindow.addEventListener(PopUpEvent.CLOSE, onAlertClose, false, 0, true);
alertWindow.width = stage.width;
alertWindow.height = stage.height / 2;
alertWindow.x = 0;
alertWindow.y = 100;
alertWindow.open(this, true);

private function onAlertClose(event:PopUpEvent):void {
    trace(event.commit);
    trace(event.data);
    alertWindow = null;
}

Creating custom dialog windows

Once you understand how to use the SkinnablePopUpContainer it is pretty simple to create any kind of dialog window you might need: confirmation messages, simple alerts, pop up lists, and so on. Here is the code for a confirmation window that has two buttons (Yes and No) and lets you set the message text:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <s:SkinnablePopUpContainer xmlns:fx="http://ns.adobe.com/mxml/2009"
  3.                                                    xmlns:s="library://ns.adobe.com/flex/spark"
  4.                                                    backgroundColor="0x898989" width="400">
  5.         <s:layout>
  6.                 <s:VerticalLayout gap="20" paddingBottom="10" paddingLeft="30" paddingRight="30" paddingTop="30" horizontalAlign="center"/>
  7.         </s:layout>
  8.         <fx:Script>
  9.                 <![CDATA[
  10.                        
  11.                         private var _message:String;
  12.                        
  13.                         [Bindable]
  14.                         public function get message():String {
  15.                                 return _message;
  16.                         }
  17.  
  18.                         public function set message(value:String):void {
  19.                                 _message = value;
  20.                         }
  21.  
  22.                         private function onClick(commit:Boolean):void {
  23.                                 super.close(commit);                           
  24.                         }
  25.                        
  26.                 ]]>
  27.         </fx:Script>
  28.        
  29.         <s:Label text="{message}" width="100%" textAlign="center"/>
  30.  
  31.         <s:HGroup width="100%">
  32.                 <s:Button label="Yes" width="50%" click="onClick(true)"/>
  33.                 <s:Button label="No" width="50%" click="onClick(false)"/>
  34.         </s:HGroup>
  35.        
  36. </s:SkinnablePopUpContainer>

And here is how this component look on my Android phone:

And here is the code for a pop up list that lets you to set the title, the list’s data provider, the list’s label field, the list’s initial selected items, and multiple selections (you can retrieve the selected items by listening for PopupEvent.CLOSE and reading the data attribute).

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <s:SkinnablePopUpContainer xmlns:fx="http://ns.adobe.com/mxml/2009"
  3.                                                    xmlns:s="library://ns.adobe.com/flex/spark"
  4.                                                    backgroundColor="0x898989" width="400">
  5.         <s:layout>
  6.                 <s:VerticalLayout gap="20" paddingBottom="10" paddingLeft="30"
  7.                                                   paddingRight="30" paddingTop="30" horizontalAlign="center"/>
  8.         </s:layout>
  9.         <fx:Script>
  10.                 <![CDATA[
  11.                         import mx.collections.ArrayCollection;
  12.                        
  13.                         private var _dataProvider:ArrayCollection;
  14.                         private var _labelField:String;
  15.                         private var _allowMultipleSelection:Boolean;
  16.                         private var _selectedItems:Vector.<Object>;
  17.                        
  18.                         [Bindable]
  19.                         public function get selectedItems():Vector.<Object> {
  20.                                 return _selectedItems;
  21.                         }
  22.  
  23.                         public function set selectedItems(v:Vector.<Object>):void {
  24.                                 _selectedItems = v;
  25.                         }
  26.  
  27.                         [Bindable]
  28.                         public function get allowMultipleSelection():Boolean {
  29.                                 return _allowMultipleSelection;
  30.                         }
  31.  
  32.                         public function set allowMultipleSelection(v:Boolean):void {
  33.                                 _allowMultipleSelection = v;
  34.                         }
  35.  
  36.                         [Bindable]
  37.                         public function get labelField():String {
  38.                                 return _labelField;
  39.                         }
  40.  
  41.                         public function set labelField(v:String):void {
  42.                                 _labelField = v;
  43.                         }
  44.                        
  45.                         [Bindable]
  46.                         public function get dataProvider():ArrayCollection {
  47.                                 return _dataProvider;
  48.                         }
  49.  
  50.                         public function set dataProvider(v:ArrayCollection):void {
  51.                                 _dataProvider = v;
  52.                         }
  53.                        
  54.                         private function onClick(commit:Boolean):void {
  55.                                 super.close(commit, list.selectedItems);                               
  56.                         }
  57.                 ]]>
  58.         </fx:Script>
  59.        
  60.         <s:Label text="Select an item:" width="100%"/>
  61.        
  62.         <s:List id="list" width="100%" height="100%"
  63.                         dataProvider="{dataProvider}"
  64.                         labelField="{labelField}"
  65.                         allowMultipleSelection="{allowMultipleSelection}"
  66.                         selectedItems="{selectedItems}"/>
  67.                
  68.         <s:HGroup width="100%">
  69.                 <s:Button label="OK" width="50%" click="onClick(true)"/>
  70.                 <s:Button label="Cancel" width="50%" click="onClick(false)"/>
  71.         </s:HGroup>
  72.        
  73. </s:SkinnablePopUpContainer>

And here is the component running on my phone:

Skinning

The two custom dialog windows I showed you in the previous section use the default look and feel. What about skinning a SkinnablePopUpContainer? There is a Flex class that provides the default skin for a SkinnablePopUpContainer: SkinnablePopUpContainerSkin. By creating a skin that extends this default skin you can change the background of the component quite easily. If you look at the SkinnablePopUpContainerSkin skin you’ll notice that it has a rect with the id equal to background. This is the starting point to change the default background. You can draw additional stuff on top of this background or replace it alltogether with an FXG or PNG file.

For the rest of the UI components used in a SkinnablePopUpContainer (buttons, text inputs, labels, and so on) you will create additional skins and/or use CSS. Here is a screenshot with the same confirmation window but this time using a custom skin:

Here is the skin class used to customize the confirmation window presented in the previous section:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark"
  3.         xmlns:fb="http://ns.adobe.com/flashbuilder/2009"
  4.                 alpha.disabled="0.5" xmlns:skins="org.corlan.dialog.skins.*">
  5.    
  6.     <fx:Metadata>
  7.     <![CDATA[
  8.         /**
  9.          * @copy spark.skins.spark.ApplicationSkin#hostComponent
  10.          */
  11.         [HostComponent("spark.components.SkinnablePopUpContainer")]
  12.     ]]>
  13.     </fx:Metadata>
  14.    
  15.     <s:states>
  16.         <s:State name="normal"/>
  17.         <s:State name="disabled"/>
  18.         <s:State name="closed" stateGroups="closedGroup"/>
  19.         <s:State name="disabledAndClosed" stateGroups="closedGroup"/>
  20.     </s:states>
  21.    
  22.     <!– Transitions for open and close –>
  23.     <s:transitions>
  24.         <s:Transition fromState="closed" toState="normal" autoReverse="true">
  25.             <s:Fade duration="150" target="{chrome}"/>
  26.         </s:Transition>
  27.  
  28.         <s:Transition fromState="disabledAndClosed" toState="disabled" autoReverse="true">
  29.             <s:Fade duration="150" target="{chrome}"/>
  30.         </s:Transition>
  31.        
  32.         <s:Transition fromState="normal" toState="closed" autoReverse="true">
  33.             <s:Fade duration="150" target="{chrome}"/>
  34.         </s:Transition>
  35.  
  36.         <s:Transition fromState="disabled" toState="disabledAndClosed" autoReverse="true">
  37.             <s:Fade duration="150" target="{chrome}"/>
  38.         </s:Transition>
  39.     </s:transitions>
  40.        
  41.     <!— Defines the background and content group used by this skin. –>
  42.     <s:Group id="chrome" left="0" right="0" top="0" bottom="0" visible.closedGroup="false">
  43.         <!—
  44.                 Defines the appearance of the SkinnablePopUpContainer class‘s background.
  45.                 In this case a FXG file is used to draw the background.
  46.                 –>
  47.                 <skins:BackGroundAlert id="background" left="0" right="0" top="0" bottom="0"/>
  48.        <!— @copy spark.components.SkinnableContainer#contentGroup –>
  49.        <s:Group id="contentGroup" left="0" right="0" top="0" bottom="0" minWidth="0" minHeight="0">
  50.            <s:layout>
  51.                <s:BasicLayout/>
  52.            </s:layout>
  53.        </s:Group>
  54.    </s:Group>
  55.    
  56. </s:Skin>

And this is the FXG used by the skin class to draw the background:

  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <Graphic version="2.0" xmlns="http://ns.adobe.com/fxg/2008"
  3.         xmlns:d="http://ns.adobe.com/fxg/2008/dt"
  4.         xmlns:flm="http://ns.adobe.com/flame/2008"
  5.         scaleGridLeft="10" scaleGridTop="10"
  6.         scaleGridRight="90" scaleGridBottom="90">
  7.       <Rect width="100" height="100" radiusX="10">
  8.         <fill>
  9.           <LinearGradient x="190.417" y="0" scaleX="361.667" rotation="90">
  10.             <GradientEntry ratio="0" color="#FFFFFF"/>
  11.             <GradientEntry ratio="0.0424137" color="#E1DBCE"/>
  12.             <GradientEntry ratio="0.0886452" color="#CEC3AE"/>
  13.             <GradientEntry ratio="0.139618" color="#C4B89F"/>
  14.             <GradientEntry ratio="0.208589" color="#C2B59B"/>
  15.             <GradientEntry ratio="0.588957" color="#C2B59B"/>
  16.             <GradientEntry ratio="0.758513" color="#C0B399"/>
  17.             <GradientEntry ratio="0.852955" color="#B2A58E"/>
  18.             <GradientEntry ratio="0.932468" color="#918372"/>
  19.             <GradientEntry ratio="1" color="#594A42"/>
  20.           </LinearGradient>
  21.         </fill>
  22.         <stroke>
  23.           <SolidColorStroke weight="3"/>
  24.         </stroke>
  25.       </Rect>
  26. </Graphic>

Download

You can download a working Flex project that includes all the code used in this article from here.

9 thoughts on “Flex Mobile Development: Creating Dialog Windows

  1. hi, i am reading your blog while i am preparing to show how to create a preference view in native android and mobile flex. :) thanks for the post

  2. Hi this Popup list works like a charm thank you, But I have noticed an error when you try to populate the array with only one record. Can you try it out and let me know

  3. Why is SkinnablePopUpContainerSkin extending the mxml skin classes ? They say mxml skin with state are a performance killer for mobile…

    This popup is a bit sluggish on iPad1, I was forced to recreate the skin base on MobileSkin.

  4. Hi Mihai,

    Thanks for this good alert window example on a Flex Mobile.

    I started to use it and would say that the implementation was quite straightforward.

    I do have a question though. In my 7″ Galaxy Tab, the alert displays the width properly, but for some reason, the same does not display on a Galaxy Note phone.

    Any area which you think I should look at?

    Thanks.

    Angelo

  5. I’d like to see a full on custom popup. For example why do you give the popup container a layout, shouldn’t that be handled by the skin class.
    I’m trying to create a popup with the popup container containing only the components and some logic, then trying to use the skin to deal with layout (as well as excluding various skin parts). Can’t seem to do it in just mxml. Would be nice to see an example of this.

  6. I am building an app for tablets(ipad,playbook) and wld like to know if this can be used to achieve a modal box dat plays a video on popup, as in putting a mediacontainer element from OSMF into the modal popup so when a user finish viewing the video, they can close the popup and launch anoda video.

Leave a Reply

Your email address will not be published. Required fields are marked *