Marc Hughes


Home
Blog
Twitter
LinkedIn
GitHub
about
I am a developer from a bit west of Boston.

A Flash Catalyst / Builder workflow method

02 Jun 2009

Update: Check out the new post where I describe how to compile to a swc.

I've been playing around with the Flash Catalyst and Builder betas today and one thing that scares me a little is the built-in designer/developer workflow that's implied.  From what I can gather, a designer uses a design tool, imports that into Catalyst, and then hands of a .FXP file that a developer then imports into Builder to work on.  Then the developer jumps into the files generated by Catalyst, and modifies them.

For smallish projects, that's a slam-dunk.  If you don't work on large projects, stop reading here and just stick with that.

But there's a number of reasons I really don't like this intended workflow for larger projects.

First, it's not scaleable.  One catalyst file -> one Flash Builder project.  It's awefully hard for a team of 5 designers to work on a single catalyst file.

It implies some exclusivity.  If the designer hands off the .FXP file, they really shouldn't continue working on it because the developer will start modifying it.  It's kind of like having a Word document that you pass back and forth between the designer and the developer.  If they both want to work on it at the same exact time, you'll get race conditions.

Second, I don't trust anybody to get a perfect Generated Code <-> User Edited Code tool to work perfectly all the time.  If Catalyst generates some code, I edit that, and then we bring it back into Catalyst, I just know that will break at some point.  (Sorry Adobe guys, I know you'll do a rocking job with it, and I know it will almost always work, but it's just too hard a problem)

So here's a solution I've been playing with:

Step 1: Create your design comp

Here's a Photoshop file I created with a pretty simple login form.  Make sure to name your layers so they're easy to find later.

Step 2: Import it into Flash Catalyst just like you're supposed to.  Edit it to your heart's content.

There's a bunch of tutorials out there (great one from Lee Brimlow here) that show how to actually use Catalyst.  But one difference from all those tutorials.  We won't be using the Main root level item.  So don't go crazy adding states to your base stage.  But DO go crazy creating sub-components and adding states and behaviors to those.

Here's a screenshot of my Catalyst project.  This is showing off a LoginForm component that I created.  Notice it has a few states with transitions set up between those states.

Step 3: Save it as a .FXP

File->Save from Flash Catalyst.

For my example, I used FCTest.fxp as the name.

Step 4: Create an empty Flash Builder project (not importing anything here)

Just create a plain old Flash Builder project with a Flex based application in it.  (Make sure you're using a Flex 4 SDK!)

Step 5: Take your .FXP from above, and unzip it somewhere

Here's the sneaky part.  That .fxp file is really just a zip file that contains a Flash Builder project.  You can unzip it with any standard zip tool.  In OSX I just open a terminal and did a:

unzip FCTest.fxp

It creates a directory structure that looks something like this:

Notice the two folders with arrows pointing to them, they're important in the next step.

Step 6: Take the components and assets folders from that unzipping and put them in a brand new folder.

We can take those two folders and use them in our Flash Builder project to get access to all of our custom components that we made in Flash Catalyst.  I'm planning ahead a little bit so I created a "CatalystAssets" folder, and a "FCTest.fxp" folder inside that, and placed them there.  This will allow me to have assets from many different catalyst files in the future someday.

Step 7: Link to that folder from your Flash Builder project

Flash Builder can link to source directories outside of the project.  To do that go to Project->Properties->Flex Build Path.  Click the "Add Folder..." button and add the FCTest.fxp folder (the folder we created, not the file Catalyst made).

In Flash Builder's Package Explorer you'll see something like this after you do that:

The developer should NEVER EDIT anything in the FCTest folder.  Consider that all auto-generated stuff that will wipe out anything you do in there.

The developer can and should work in the normal Flash Builder src folder, likely importing classes and assets from the FCTest folder and using them.

Step 8: Use the Flash Catalyst based assets!

For simple components that you don't need to add any functionality to, you can just use them in your MXML.  For example, I created a component called Background in my Catalyst file.  To use it in the Flex application you just do something like this:

<?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" minWidth="1024" minHeight="768"
 xmlns:components="components.*">

<components:Background verticalCenter="0" horizontalCenter="0" />

</s:Application>

Notice the components namespace with the Background component name.

Adding non-interactive components isn't all that interesting.  So what happens if you want to add functionality to components created in catalyst?  There's two options here, either use the assets from the .FXP through composition or inheritance.  Here are two examples that show how to do them both:

Extending a Catalyst component through inheritance:

package com.roguedevelopment.fctest
{

import components.LoginForm; import flash.events.MouseEvent; import flash.utils.setTimeout; import mx.controls.Alert; import mx.events.FlexEvent;

public class InheritenceExample extends LoginForm { public function InheritenceExample() { super(); addEventListener(FlexEvent.CREATION_COMPLETE, creationComplete ); } protected function creationComplete(event:FlexEvent):void { setTimeout( function():void{ currentState = "Normal"; }, 300 ); textinput2.displayAsPassword = true; button1.addEventListener(MouseEvent.CLICK, onLoginButtonClick ); textinput1.setFocus(); }

protected function onLoginButtonClick(event:MouseEvent):void { var correct:Boolean = ( textinput1.text == "marc" ) && ( textinput2.text = "1234" );

if( correct ) { currentState = "Working"; setTimeout( success , 1500 ); } else { currentState = "Error"; setTimeout( function():void{ currentState = "Normal"; }, 1500 ); } }

protected function success() : void { Alert.show("Correct log in!"); currentState = "Normal"; } } }

Or, if you want to use composition instead of inheritance you could do something like this:
package com.roguedevelopment.fctest
{
  import components.LoginForm;
  import flash.events.MouseEvent;
  import flash.utils.setTimeout;
  import mx.controls.Alert;
  import spark.components.Group;

public class CompositionExample extends Group { protected var loginForm:LoginForm;

public function CompositionExample() { super(); }

override protected function createChildren() : void { loginForm = new LoginForm(); addElement(loginForm); width = 527; height = 325; setTimeout( function():void{ loginForm.currentState = "Normal"; }, 300 ); loginForm.textinput2.displayAsPassword = true; loginForm.button1.addEventListener(MouseEvent.CLICK, onLoginButtonClick ); loginForm.textinput1.setFocus(); }

protected function onLoginButtonClick(event:MouseEvent):void { var correct:Boolean = ( loginForm.textinput1.text == "marc" ) && ( loginForm.textinput2.text = "1234" );

if( correct ) { loginForm.currentState = "Working"; setTimeout( success , 1500 ); } else { loginForm.currentState = "Error"; setTimeout( function():void{ loginForm.currentState = "Normal"; }, 1500 ); } }

protected function success() : void { Alert.show("Correct log in!"); loginForm.currentState = "Normal"; } } }

Now that you have your components with some added functionality, you can use them in your main application like you would any other Flex based component. You can see a working demo, with view-source enabled here:

http://rogue-development.com/uploads/catalystWorkflow/FCTest.html

As you can tell, that seems like a LOT of extra work. But here's the payoff:

  1. The designer is free to continue working in Flash Catalyst while the developer works in Flash builder.  Whenever he has changes, he just hands off a brand new .FXP, and the developer repeats steps 5 and 6 to replace some files and then refreshes the Flash Builder workspace.
  2. You can create multiple folders inside that "CatalystAssets" folder, and then use assets from multiple FlashCatalyst projects in a single Flash Builder project.
  3. You can add those Flash Catalyst assets to version control and easily distribute them to multiple developers. (Not to mention track changes!)
I did up a quick shell-script to unzip the .fxp file, make some directories, and copy the files to the appropriate place.  I can run this whenever the designer gives me a new .fxp. I think with a little work we could create a Eclipse "Builder" (that's unrelated to the product name "Flash Builder") that would completely automate this entire process.
#!/bin/bash

rm -rf .tmp mkdir .tmp cd .tmp

unzip -o ../FCTest.fxp

cd ..

rm -rf CatalystAssets/FCTest.fxp/com/roguedevelopment/fctest

mkdir CatalystAssets/FCTest.fxp mkdir CatalystAssets/FCTest.fxp/assets

mv .tmp/src/components CatalystAssets/FCTest.fxp mv .tmp/src/assets/FCTest CatalystAssets/FCTest.fxp/assets

Some things I wish Flash Catalyst did:
  1. Let me select a package that components get created in. (maybe there's a way to do that?)
  2. Make it easier to intelligently name the classes and property names that get generated
  3. Export a swc with everything in it so we don't have to go through most of this.
Well anyways, I give Catalyst 2 thumbs up and even if we have to go through this process for a sane workflow it will save huge amounts of time in our shop.

P.S. I'm no Catalyst expert so maybe there IS a better workflow that I just haven't seen yet.