Create a Flickr Viewer using Flex

by Stéphane Schittly 12

In this tutorial, you will learn how to integrate photos from Flickr in your Flex application, using as3flickrlib.

You will need a Flickr API KEY , Adobe Flex Builder 3 or Eclipse with Flex Builder 3 plugin, and some basic knowledge of Flex and actionscript.

You can view the final application here (View Source is enabled).

All the source files are here .

Part 1 : Creating the Flex project.

Create a new Flex project called ‘FlickrViewer’, and choose ‘Web application’. Click on ‘Next’. Then, leave the output folder as default, it should be ‘bin’. Click on ‘Next’.

Part 2 : Design the user interface.

The project is now ready to go. We will first designing the application.

Open ‘FlexViewer.mxml’. Go to the Design Mode. Click on the application (it should be a blue square taking all the page) and go the Properties panel. Go to Layout and choose vertical in the comboBox. Then go to Components panel, and in ‘Layout’ select the ApplicationControlBar, then drag it in the Application. Go to Flex Properties and set a width of 100% and remove everything in height (the height of the ApplicationControlBar will be the height of its contents).

Designing Application

Drag a ‘TextInput’ component in the ApplicationControlBar and a ‘Button’ component just after the TextInput. Set the label of the button to ‘SEARCH FLICKR’, in the Flex Properties panel. Add a TileList component next of the ApplicationControlbar. Your application should look like that :

Application Designed

Now set up the ID parameter of the TextInput, Button and TileList components in the Flex Properties panel. TextInput should be set up to ‘txtSearch’, the Button should be set up to ‘btnSearch’ and the TileList to ‘picsList’.

The user interface is completelly designed, let’s add Flickr.

Part 3 : The libs.

First, you will have to download the compiled versions of 2 libraries :

as3FlickrLib and asCoreLib. Go to http://code.google.com/p/as3flickrlib/ to get the first lib. Download the package (featured downloads). Unzip the file, and the in the expanded folder, go to bin/ and drag the file in Flex, inside your project, in the src folder. Once it’s done, go to http://code.google.com/p/as3corelib/ and repeat the operation. You should see that in your project : and some other files of course.

Fig 3
So now your project has the 2 libs.I strongly recommends you to use that method in any projects. By doing that, you are sure that you always have a working library. If you want to make a big application using external libraries (which may evolve), I recommend you to use Flex Library projects and SVN synchronization, it will allow you to keep your code up to date.

To complete that part, you need to tell Flex that you will use those libs. Right-click on the project name, and then go to ‘properties’ -> ‘Flex Build Path’ and then -> ‘Library path’ and the ‘add SWC’. Choose corelib.swc and do the same for flickr.swc.

Part 4 : The code.

In Code Mode, add a line after the tag. Start typing “<> Mx:Script’. Press the Enter key. Now you should have a complete script tag. Inside of this Script tag, include the flickr functions we need :

import com.adobe.webapis.flickr.*;
import com.adobe.webapis.flickr.events.*;

Then, some variables :

private var flickr:FlickrService;
private var api_key:String = "enter you Flickr API key here";
private var maxResult:Number = 50;

After that, security parameters :

Security.allowDomain(["api.flickr.com", "flickr.com", "*"]);
Security.allowInsecureDomain(["api.flickr.com", "flickr.com", "*"]);

And set the dataProvider of the TileList :

import mx.collections.ArrayCollection;
[Bindable] public var photos:ArrayCollection;

‘Bindable’ means that the TileList will update each time the dataProvider change.

After that the function to call Flickr webservice. Basically, that means that we set up the api_key to send, an eventListener to know when the data comes back, and the request itself to search photos, based on the TextInput value.

private function getImages():void {
var service:FlickrService = new FlickrService( api_key );
service.addEventListener( FlickrResultEvent.PHOTOS_SEARCH, onPhotosSearch );
service.photos.search("", txtSearch.text, "any", "", null, null, null, null, null, "", maxResult,1 );
}

Now that the code is done, we just need to connect the TileList to the data. Go to the TileList tag, and at the end of the parameters, just add ‘dataProvider={photos}’. The tag should now look like that :

<mx:TileList width="100%" height="100%" id="picsList" dataProvider="{photos}" itemRenderer="iconRenderer" >

If you run the application now, you wont see any images. It’s because we miss an itemRenderer for the image.

Part 5 : The necessary itemRenderer

Go to ‘File’ -> ‘New’ -> ‘MXML Component’. Type ‘iconRenderer’ as filename and choose ‘Box’ in ‘Based on’. Remove width and height.

Go to Design Mode. Drop an Image Component. In the Flex Properties panel, set the ID to ‘pic’.

Go to Code Mode. Add a script tag after the first Tag. In this script tag, add the following lines :

import com.adobe.webapis.flickr.Photo;
import mx.core.Application;
public function init():void{
pic.source = 'http://static.flickr.com/' + data.server + '/' + data.id + '_' + data.secret + '_m.jpg';
//trace("source : "+pic.source)
}

Then, add an updateComplete event listener to this init function in the Box tag. It should look like that :

The itemRenderer is not ready. The complete code should be this :

Part 6 : Linking the itemRenderer to TileList

Go back to FlickrViewer.mxml. In Code Mode, go to the TileList tag and add the itemRenderer parameter by typing :

itemRenderer=”iconRenderer”

The complete code for FlickViewer is done and should look like this :

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical">
<mx:Script>
<![CDATA[
//import mx.messaging.channels.StreamingAMFChannel;
import com.adobe.webapis.flickr.*;
import com.adobe.webapis.flickr.events.*;
private var flickr:FlickrService;
private var api_key:String = "enter you Flickr API key here";
private var maxResult:Number = 50;
private var service:FlickrService = new FlickrService( api_key );

Security.allowDomain(["api.flickr.com", "flickr.com", "*"]);
Security.allowInsecureDomain(["api.flickr.com", "flickr.com", "*"]);

import mx.collections.ArrayCollection;
[Bindable] public var photos:ArrayCollection;

private function getImages():void {
  var service:FlickrService = new FlickrService( api_key );
  service.addEventListener( FlickrResultEvent.PHOTOS_SEARCH, onPhotosSearch );
  service.photos.search("", txtSearch.text, "any", "", null, null, null, null, null, "", maxResult,1 );
}

private function onPhotosSearch( event:FlickrResultEvent ):void {
  var photoList:PagedPhotoList = event.data.photos;
  photoList.perPage = maxResult;
  photos = new ArrayCollection( photoList.photos );
}
]]>
</mx:Script>
<mx:ApplicationControlBar width="100%">
<mx:TextInput id="txtSearch"/>
<mx:Button label="SEARCH ON FLICKR" id="btnSearch" click="getImages()"/>
</mx:ApplicationControlBar>
<mx:TileList width="100%" height="100%" id="picsList" dataProvider="{photos}" itemRenderer="iconRenderer" >
</mx:TileList>
</mx:Application>

And the final code for iconRenderer.mxml :

<?xml version="1.0" encoding="utf-8"?>
<mx:Box xmlns:mx="http://www.adobe.com/2006/mxml" updateComplete="init()">
<mx:Script>
<![CDATA[
import com.adobe.webapis.flickr.Photo;
import mx.core.Application;
public function init():void{
pic.source = 'http://static.flickr.com/' + data.server + '/' + data.id + '_' + data.secret + '_m.jpg';
//trace("source : "+pic.source)
}
]]>
</mx:Script>
<mx:Image id="pic" width="100" height="100" />
</mx:Box>

Part 7 : click to enlarge

Now the users can watch a list of pictures. That’s ok but not very good.

To allow users to enlarge the pics, we will first create a new component, wich will contain the big pic. Go to File > New > MXML Component. Enter ‘bigPic.mxml’ as filename and ‘TitleWindow’ in ‘based on’. Enter ‘100%’ for both width and height. Now, change the first tag by this one :

<mx:TitleWindow xmlns="*" xmlns:mx="http://www.adobe.com/2006/mxml"
    xmlns:controls="qs.controls.*" xmlns:smooth="imageControls.*"
    xmlns:eff="com.adobe.effects.*" xmlns:views="views.*"
    layout="vertical"
    height="100%" width="100%"
    title="CLICK ANYWHERE TO CLOSE" textAlign="center" color="#333333"
    backgroundColor="#000000" backgroundAlpha="0.9"
    borderThicknessLeft="0" borderThicknessRight="0" borderThicknessTop="0" borderThicknessBottom="0"
    headerHeight="25" verticalScrollPolicy="off" horizontalScrollPolicy="off"
    paddingBottom="0" paddingLeft="0" paddingRight="0" paddingTop="0"
    creationComplete="init()">

The ‘xmlns’  are references to libs we will use. There are some graphical stuff, like borderThickness, you can read Flex Documentations to know more, they are basically use to fit the browser’s window.

Go back to FlickViewer.mxml. To launch this component when the user clicks on an item in the list, add this code in the Script tag, at the end :

///// BIGPIC FUNCTIONS
            import mx.managers.PopUpManager;
            import mx.containers.TitleWindow;
            import bigPic;
            import mx.events.FlexMouseEvent;
            import mx.core.Application;

            public var fullWindow:bigPic;

            public function showBigPic(id:String):void {
                fullWindow = bigPic(PopUpManager.createPopUp(this, bigPic, true));
                fullWindow.selectedPhoto = id;
                fullWindow.selectedPhotoNum = picsList.selectedIndex;
            }

Then look in the mxml code, and find the TileList tag. in the tag, add the itemClick property to launch the big pic :

itemClick="showBigPic(event.itemRenderer.data.id)"

The tag should now look like that :

<mx:TileList width="100%" height="100%" id="picsList"
        dataProvider="{photos}" itemRenderer="iconRenderer"
        itemClick="showBigPic(event.itemRenderer.data.id)" >

Now, let’s add some layout. Go back to bigPic.mxml, and between the two tags on the stage, add this code :

<mx:Canvas height="100%" width="100%">
        <mx:Canvas x="0" y="0" id="preload" width="100%" height="100%" click="removeMe()" >
                <mx:Box width="100%" height="100%" verticalAlign="middle">
                    <mx:ProgressBar id="preloadBar" source="daPic" mode="event" width="100%"
                        complete="preload.visible = false; img.visible = true;" labelPlacement="bottom"/>
                        <mx:Label id="imgTitle" width="100%" textAlign="center"
                            text="{Application.application.photos.getItemAt(Application.application.selectedPhotoNum).title}"
                            fontSize="25" color="#666666" />
                    <mx:Image id="daPic2" source="{photoUrl}" visible="false" width="1" height="1" />
                </mx:Box>
        </mx:Canvas>
        <mx:Canvas x="0" y="0" width="100%" height="100%" id="img"
            showEffect="{fadeAndBlurIn}" hideEffect="{fadeAndBlurOut}"
            verticalScrollPolicy="off" horizontalScrollPolicy="off">
            <mx:HBox width="100%" height="100%" horizontalAlign="center" verticalAlign="middle">
                <mx:Box id="prevBox" visible="true" width="100" height="100%" horizontalAlign="center">
                    <mx:Spacer height="50%" />
                    <mx:Button width="70" label="prev" click="changePic('prev')" />
                </mx:Box>
                <mx:Box width="100%" height="100%"
                    verticalAlign="middle" horizontalAlign="center" click="removeMe()">
                    <mx:Image id="daPic" source="{photoUrl}"
                        verticalAlign="center" horizontalAlign="middle"
                        width="100%" height="100%" cacheAsBitmap="true"  />
                </mx:Box>
                <mx:Box id="nextBox" width="100" height="100%" bottom="10%" horizontalAlign="center">
                    <mx:Spacer height="50%" />
                    <mx:Button width="70" label="next" click="changePic('next')" />
                </mx:Box>
            </mx:HBox>
        </mx:Canvas>
    </mx:Canvas>

It’s a canvas containing 2 Canvases. The first one contains a ProgressBar, which will show image loading progression. The ‘onComplete’ event in the tag set this canvas to invisible at the end of the loading. By being invisible, we will be able to see the second Canvas, which contains the image.

Inside the second Canvas, you can see 3 Boxes. The first one contains a Button, the next one the image, and the third one another Button. That’s how your application will look like : a big pic surrounded by two navigation buttons. We will see the code in part 9.

There is in each Canvas a ‘showEffect’ and ‘hideEffect’ parameter. It’s made to make a nice fade between the preloader and the photo. But the effects are not here. We will add them right now.

Part 8 : Adding fade effect

Add the following code just after the first tag (TitleWindow ) :

<mx:Sequence id="fadeAndBlurIn" startDelay="0" >
        <mx:Fade id="fadeIn" duration="1200" alphaFrom="0.0" alphaTo="1.0" />
    </mx:Sequence>
    <mx:Sequence id="fadeAndBlurOut" startDelay="0">
        <mx:Fade id="fadeOut" duration="300" alphaFrom="1.0" alphaTo="0" />
    </mx:Sequence>

All effect needs to be set after the first tag, and it’s mandatory, as well as for Scripts and Style tags.

Part 9 : Adding code to load the big pic, and manage the navigation

After the first tag, add this code :

<mx:Script>
        <![CDATA[
            import mx.core.Application;
            import mx.rpc.soap.LoadEvent;
            import mx.managers.SystemManager;
            import mx.managers.PopUpManager;
            import mx.utils.ArrayUtil;
            import mx.collections.ArrayCollection;

            import com.adobe.webapis.flickr.methodgroups.People;
            import com.adobe.webapis.flickr.methodgroups.PhotoSets;
            import com.adobe.webapis.flickr.FlickrService;
            import com.adobe.webapis.flickr.*;
            import com.adobe.webapis.flickr.events.*;

            [Bindable] private var photos:ArrayCollection;
            [Bindable] private var photoUrl:String;
            [Bindable] private var photoFlickrUrl:String;
            [Bindable] public var selectedPhotoNum:Number;
            [Bindable] public var selectedPhoto:String;

            public function init():void
            {
                img.visible = false;
                this.width = stage.width;
                this.height = stage.height;
                getPhotoSize();
            }

            private function removeMe():void {
                PopUpManager.removePopUp(this);
            }

            private function getPhotoSize():void{
                trace("getPhotoSize: "+selectedPhoto)
                preload.visible = true
                img.visible = false;
                var service:FlickrService = new FlickrService( Application.application.api_key );
                   service.addEventListener( FlickrResultEvent.PHOTOS_GET_SIZES, onPhotoSizeSearch );
                   service.photos.getSizes( Application.application.photos[selectedPhotoNum].id );
            }

            private function onPhotoSizeSearch( event:FlickrResultEvent ):void {
                var photoSize:PhotoSize = event.data["photoSizes"] as PhotoSize;
                imgTitle.text = Application.application.photos[selectedPhotoNum].title
                var result:Array = event.data.photoSize;
                photoUrl = event.data["photoSizes"][(event.data["photoSizes"].length-1)].source;
            } 

            public function changePic(operation:String):void{
                if(operation == "next"){
                    if(selectedPhotoNum < Application.application.photos.length-1){
                        selectedPhotoNum++;
                    }else{
                        selectedPhotoNum = 0;
                    }
                }else{
                    if(selectedPhotoNum > 0){
                        selectedPhotoNum--;
                    }else{
                        selectedPhotoNum = Application.application.photos.length-1;
                    }
                }
                getPhotoSize();
            }

        ]]>
    </mx:Script>

It sure looks quite long, but it’s really simple.

The init() function react to the ‘OnCreationComplete’ event in the first tag. The first line hide the ‘img’ canvas, so we dont see it during the loading. The two second lines simply fit the component to the browser window, and the last line launch the loading of the first image.

The getPhotoSize() function is the core of this component. It will search all the available sizes for the image.

The onPhotoSizeSearch() function is executed when the getPhotoSize() is done. The aim of this function is to set the text for the label in the preloader canvas, and – most important – get the URL of the biggest image available. It’s this line :

photoUrl = event.data["photoSizes"][(event.data["photoSizes"].length-1)].source;

‘photoSizes’ is an array retured by Flickr, which define the sizes from the smallest to the biggest.

And finally, the changePic() function simply handle the navigation. If you look closely in the mxml code, you will see two buttons. When clicked, this function is called and just add or remove 1 to selectedPhotoNum, a variable that getPhotoSize() use to know which image to load. Of course, we add some conditions to prevent errors due to undefined id (because if it happens, the application will sure crash :-)).

Part 10 : Launch your application and enjoy !

Click on the run application button, and in the browser, type a word in the search box. Normally, you should see images appearing. If you see them, you just manage to make your first Flickr / Flex mashup. Congrats !

Now, look further : you can do a lot with that. Flickr has a whole range of functions that you can discover here . You can also refer to the asflickrlib docs on google code . You can do almost everything that users do on the Flickr website.

Plus, you can add more effects, or even do crazy stuff with papervision, physics engines… The possiblities are unlimited. All you need to find now is the revolutionnary idea to do something with all those pictures !

You only need to consider one limitation : Flickr can be slow. And by doing that, you can’t really manage the speed of your application. So avoid to load too much photos on each request. Use the max_result parameter to limit this amount.

This concludes my first tutorial. Thank for reading that to the end. I hope that you enjoy reading it and that it will help you. To thank me, just drop a line in the comments, make a link on your website or, even better, send me a picture of you drinking a beer in your local pub 🙂

Comments (12)

  1. Hi, thanks for this tutorial – it went relatively smoothly, except you need to make ‘api_key’ a public variable, not as private defined in ‘Part 4’ (above). I notice that you have it as a public variable in your working source code example.

    1. I am a newbie to this, i got all the design views working but when i ran my app, i got this link and was unable to get a result from my link. pls how do i change this. file:///C:/Documents%20and%20Settings/Adetunji/Desktop/flash%20portfolio/flex/Flickrview/bin-debug/Flickrview.html

  2. You’re right 🙂 Sorry for the mistake.

  3. Hi Stéphane Schittly!
    I have been downloaded your source code, then i run it in my computer. I got result okie for all.
    But i upload it to host: http://irabbit.vndv.com, the application not run and i get error for security sandbox. I register api key for http://irabbit.vndv.com and my key is 34c9a4863c7ec578b146bc46ea13186f.

    The error:
    Error #2044: Unhandled securityError:. text=Error #2048: Security sandbox violation: http://irabbit.vndv.com/flickr/main.swf cannot load data from http://flickr.com/services/rest/?api_key=34c9a4863c7ec578b146bc46ea13186f&method=flickr.photos.search&user_id=&tags=sunset&tag_mode=any&text=&min_upload_date=&max_upload_date=&min_taken_date=&max_taken_date=&license=1&extras=&per_page=50&page=1&sort=date-posted-desc&amp;.

    You can show me why it is not run and get above error. I have loaded policy file already.

    Thanks much.

  4. Thanks for a great tut, one issue I’m having is that even though I get no errors, if I run it it finds nothing! If I download a compiled version it runs fine. Very odd.

  5. Hi,

    First of all great tutorial!

    I ahve basically done everything you have stated but it seems that when I click a picture to maximise it – the image never loads.

    I’m pretty sure it must be something to do with the selectedPhoto variable not being tracked in bigPic.mxml – however im not getting any compile errors.

    What I might do is move my flickrViewer into the same src folder as the others (at the moment it appears in a popup window). I’ll let you know how this gets on.

  6. Right well I think it must be the fact that I have opened the FlickrViewer in a new popup – and then the fullsize popup is looking for the selectedPhotoNum in the Application.application – when in fact its not on the main application but on a component – ill do some more digging to see if I can come up with something.

    If theres anyone here who has managed to get this working in a pop-up i’d very much welcome your help :D.

  7. Hi Thanks for your wonderful tutorial

  8. Merci beaucoup pour cette excellente documentation!

    Thank you for this great document!

    Denis

  9. Hey! Thanks for the tutorial, it works great!

    I am trying to add a text component which would show the title of the picture retrieved. Could you please give me hints?

    Cheers,

    Tom

  10. Thanks for this tutorial.

    It’s probably worth mentioning that the button needs a “click” event handler pointing to getImages(). It’s shown in the complete code example but not mentioned before.

  11. Hi,

    I have created a sample project from your code.
    But now I want to show images from my flickr account.
    Then what should be the url in iconRenderer.mxml file?

    Thanks,
    Jigar

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>