How to build mobile widgets
by Ryan Carson, founder of Carsonified

If you're a web developer or designer, there's a really exciting development happening right now called 'mobile widgets'. Carsonified is working with Betavine to spread the word about mobile widgets, so we recently spent four days building one in order to see how hard or easy they are to build. The result was Twiggy, a Twitter search widget, and today we're going to talk about how we built it.

So what is a mobile widget?

A mobile widget is a simple web app that's built with open web technology like HTML, CSS and JavaScript (we used jQuery). You don't have to write a line of Java or any proprietary code and you don't have to understand anything about 'mobile development'. The files are packaged up into a zip file and downloaded to the phone. The device installs the widget locally, and then it can talk to the web. Widgets can currently run on Nokia S60 phones (currently 1M+ and growing).

Widgets have access to a permanent storage facility for settings and downloaded data. This mechanism is similar to cookies, but the storage capacity is larger and does not automatically expire after a given time.

Why are they a good idea?

We think mobile widgets are a really good idea because they allow you to reach a huge non-iPhone audience. Don't get me wrong, everyone at Carsonified has iPhones and we love The Steve, but we're not 'normal' and as web designers and developers, we have to consider there are a heck of a lot of people out there who would really benefit from useful widgets that ran on their phones. (They also run on Opera.)

Also, you can easily port mobile widgets to iPhone with a few simple tweaks. Here's the version of Twiggy that works on an iPhone.

Here's a quick video to show widgets in action, on a Nokia N96:

Cold hard cash

To encourage people to try building mobile widgets, Betavine are running a competition with a prize of £20,000. There's no obligation to Betavine, you keep the IP to the code and they promote the widget to Vodafone customers, so it's a pretty nifty deal.

What's in a widget?

As I mentioned above, a widget is built with HTML, CSS and JavaScript and packaged as a regular zip file, renamed to use the extension .wgt.

Here's what's in the zip:

  1. A widget configuration file. This is an XML file in the root of the widget structure that holds information about your widget including its size, name, author, and security information.
  2. An index document. Like on a web page, this document contains the basic skeleton/content of the widget. Widgets content can be created using any markup that browsers handle natively, for example HTML, SVG, or XML files. This file also lives in the root of the widget structure.
  3. Images. These are contained in a single images folder.
  4. JavaScript files. These are contained in a single script folder.
  5. Stylesheets. These are contained in a single style folder.

I'm going to hand it over to Elliott Kember, who built Twiggy, to go into detail about how Twiggy was built, and what hurdles we had to overcome. Over to you bud ...

Mobile widget tutorial

Thanks, Ryan!

Hey everyone, I'm Elliott and I built Twiggy. I'm going to take you through a brief walk-through of how to build a widget.

First, you'll need a directory. In that directory, make an "index.html" file, a "style.css" file, a "scripts" directory, and an "images" directory. You can structure your files any way you feel most comfortable with. If you want to split your CSS files up by contents later on, that's fine.

Now, in your HTML file, put some content. For our widget, we're using this:


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
  "http://www.w3.org/TR/html4/strict.dtd">
<html>
  <head>
    <title>Twiggy</title>
    <!-- This is for the Opera Widget Emulator. -->
    <script type="text/javascript">if(window.parent&&
    parent.emulator)parent.emulator.begin(window)</script>
    <link rel="stylesheet" href="style.css">
   </head>
   <body>
  </body>
</html>

You can open it in a browser, and see what it looks like (it should be blank). Now, get a copy of jQuery and put it in your "scripts" directory, and add a line like this to your to include it.


<script type="text/javascript" src="scripts/jquery-1.3.2.min
.js"></script>

Write all your application's javascript in an "actions.js" file, which goes in your /scripts folder. Add it like this:


<script type="text/javascript" src="scripts/actions.js">
</script>

If you want to use any other jQuery plugins, drop them in the /scripts folder and include them as normal:


<script type="text/javascript" src="scripts/plugin.jquery.js">
</script>

Now, add these lines to your <head>, just in case you ever want to look at the page on your iPhone:


<meta name="viewport" content="initial-scale=1,
maximum-scale=1,minimum-scale=1 user-scalable=no,width = 320" />
<meta name="viewport" content="width=device-width; 
initial-scale=1.2; maximum-scale=1.2; user-scalable=0;" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta names="apple-mobile-web-app-status-bar-style" 
content="black-translucent" />

They tell the iPhone how to render the page, what level of zoom to use, and basically tell it to treat the page like a web-app.

In your <body>, insert the following divs:


<div id="home" class="panel">
  <form id="main-search" class="panel" action="#">
    <label for="search">What is your name?</label>
    <input id="search" />
    <input type="submit" id="bigsubmit" value="Go!"/>
  </form>
</div>
<div id="results" class="panel">
  <!-- result goes in here -->
</div>

Inside the body, we've put two divs with class "panel". These are different pages of our application, and only one will show at a time. When we submit the form on the "home" panel, we want the home panel to hide, and the "results" panel to show.

The JavaScript

Now for some actions.js Javascript code:


var search = function(text){
  text = "Hello, "+text+"!";
  $('#home').hide();
  $('#results').text(text);
  $('.panel#results').fadeIn('fast');
  return false;
};

$('document').ready(function(){
  $('#search').focus();
  $('#results').hide();
  $('#main-search').submit(function(){
    value = $('#search').val();
    search(value);
  });
});

Rad! We've got ourselves a widget.

Making it look pretty

Now let's add some style:


*{outline: none !important;}
input::-moz-focus-inner { border: 0 !important; }

html, body {
  padding: 0;
  margin: 0;
}

body {
  display: block;
  width: 100%;
  font-family: verdana;
  background: #007FC0;
  text-align: center;
  color: white;
}

#home.panel {
  padding: 20px 0;
  display: block;
}

#results.panel {
  background: #ff00ff;
}

#search {
  margin-top: 10px;
  padding: 4px;
  border: 1px solid #ff00ff;
  font-size: 13px;
}

And we've got the beginnings of a widget.

The configuration

Now, all you have to do is add a config.xml file, which looks like this:


<?xml version='1.0' encoding='UTF-8'?>
<widget dockable="yes">
  <widgetname>My Awesome Application</widgetname>
  <description>It asks you your name, and says hi!</description>
  <icon src="icon_64.png" />
  <width>240</width>
  <height>320</height>
  <author>
    <name>John Q. Developer</name>
  </author>
  <id>
    <name>My Awesome Application</name>
    <revised>2009-04-08</revised>
  </id>
</widget>

And you've got it. Feel free to open that in Opera just to test it out.

Searching Twitter

Now, if you want to search Twitter, include the plugin from tweet.seaofclouds.com after your jquery javascript file:


<script type="text/javascript" src="scripts/jquery.tweet.js">
</script>

While you're there, change your label to:


<label for="search">What are you looking for?</label>

Change your actions.js file to:


var search = function(text){
  $('#home').hide();
  $('#results').fadeIn('fast');
  $("#results").tweet({
              query: text,
              join_text: "auto",
              avatar_size: 32,
              count: 10,
              loading_text: "loading tweets...",
              auto_join_text_reply: '', auto_join_text_default:
              "", auto_join_text_ed: "", 
              auto_join_text_ing: "", auto_join_text_reply: 
              "", auto_join_text_url: "",     
  });
  return false;
};

$('document').ready(function(){
  $('#search').focus();
  $('#results').hide();
  $('#main-search').submit(function(){
    value = $('#search').val();
    search(value);
    return false;
  });
});

And add this to your CSS:


#results { float: left; display: block; width: 100%; }
ul { margin: 0; padding :0; }

li {
  list-style-type: none;
  text-align: left;
  background: white;
  color: #333;
  line-height: 1.5;
  border-top: 1px solid #007FC0;
  float: left;
  display: block;
  padding: 5px 0;
  font-size: 12px;
  width: 100%;
}

li img {
  float: left;
  display: block;
  margin: 5px 8px 5px 5px ;
}

And you're done! Zip everything in that folder up into a file called "search.wgt" (zip -r search.wgt *) and put it on your phone via USB cable. When you run it, it'll run as a widget.

Testing with an emulator

Go get the widget emulator and try it. It should look like this:

The good, the bad and the ugly

This is a cool experiment, because you now have a site that will work in just about anything. The iPhone will handle it, it'll work as a dashboard slice, browsers like it, and it can be run as a widget. We've tried to port it to a PhoneGap app for the iPhone, but no success yet.

There's really not too much to watch out for. You do, however, have to be careful with your 100% width layouts so that the application doesn't scroll horizontally.

Here are some other gotchas:

  • Auto-focus input fields wherever logical. Nobody likes using that !$?*! joystick mouse.
  • Hover effects are important. Make buttons obvious, because getting to them is a nightmare.
  • Make sure the phone has your typeface, and renders it nicely. I got caught out with italics.
  • Don't resize images with HTML - although this is pretty good advice anyway. The phone does it wrong, and it looks horrible.
  • Even if you think you're only bundling this on the phones, use CSS sprites - because once you're done, you have a web app waiting to happen, and waiting for your button images to load on hover is lame.
  • There's no keypress-type event handling, which is a damn shame if you're making a game. Poll instead.
  • Remember, the phone is fast, but not as fast as your computer is. Neither is the network connection, so be thrifty.

Data storage for widgets

Storing stuff on the phone is done through some Javascript API calls. You don't have access to anything juicy yet, but you can store and retrieve strings, using the widget.getPreferenceForKey() and widget.setPreference() methods. To be clever, include the jQuery Cookie plugin and do this:


var get_key = function(key){
  if (typeof(widget) != &#039;undefined&#039;){ // A phone!
    return widget.preferenceForKey(key);
  }else{ // It&#039;s a browser!
    return $.cookie(key);
  }
}

var set_key = function(value, key){
  if (typeof(widget) != &#039;undefined&#039;){
    return widget.setPreferenceForKey(value, key)
  }else{
    return($.cookie(key, value));
  }
}

This way, you can set and get keys without worrying about whether you're saving to a cookie, or to the phone. Happy hunting!

Win £20,000 - design the best mobile web widget Sign up and learn more