Our website is made possible by displaying online advertisements to our visitors. Please consider supporting us by disabling your ad blocker.

Building a Simple Web Application in Dart

TwitterFacebookRedditLinkedInHacker News

Dart is a programming language developed by Google and made popular by Flutter, their mobile development framework for cross-platform application development.

The Dart language is a general-purpose language, built to be used for far more than just mobile development, and, in this short tutorial, I will show you how to build a basic web application, using Dart.

Things You Need to Know

I’m going to assume that you are comfortable with web application development and understand things like the DOM, HTML, CSS and have some basic JavaScript background. If you are new to web development, I recommend that you start with the Dart documentation that explains concepts such as the DOM in detail.

If you are comfortable writing HTML, CSS and very basic JavaScript, you’ll easily work through this tutorial and pick up some Dart skills along the way. For the curious, the Dart language tour is a great way to quickly learn enough about the Dart language to start using it, especially if you have previous programming experience in other languages like C#, Java, JavaScript or Python.

As we work through the tutorial, I’ll introduce some Dart programming concepts and point you to the relevant documentation, should you require it. There’s no need for you to know the Dart language to get started with this tutorial.

Getting Dart Installed and Configured for Web Development

Dart comes with an easy to follow installation guide, for each supported platform, that you can find at the official Dart Language website. There are detailed instructions for Windows, Linux and Mac. For this tutorial, you should install the Dart SDK.

The Dart SDK contains a number of useful command-line tools:

  • dart - The Dart Virtual Machine (VM)
  • dartdoc - An API documentation generator
  • dart2js - A Dart to Javascript compiler, which allows you to target the web with Dart
  • dartfmt - A code formatter for Dart
  • dartanalyzer - A static code analyzer for Dart
  • pub - The Dart package manager
  • dartdevc - A very quick Dart compiler, useful for web development

Once you have Dart installed, as per your platform instructions, open a command prompt and execute the following command:

dart --version

You should see something similar to this:

Dart VM version: 2.2.0 (Tue Feb 26 15:04:32 2019 +0100) on "macos_x64"

Now that Dart is installed, let’s set up the CLI tools we’ll be using. The Dart package manager, pub, makes it easy to add and manage Dart packages. We’ll use pub to install the command-line interface tools.

First, we install the web development tools:

pub global activate webdev

You can test that webdev is working, by running the webdev command:

webdev

Then we install Stagehand, a Dart scaffolding tool:

pub global activate stagehand

Make sure stagehand is working, by running the following command:

stagehand

Our First Web development Project

Let’s create a simple web development project, using the stagehand scaffolding tool. The advantage of using this tool is that it’ll perform a lot of the grunt work for us. Setting up a new project in Dart requires some predetermined folders to be created, and stagehand makes that a breeze.

For our first project, we’ll create a very simple web app. To have stagehand create the scaffold for our project, we must first create a new folder that will house the project. I’m going to use dartweb as my project folder. You are welcome to create any empty folder you wish and navigate to it.

Once the folder is created and you’ve navigated to inside it, run this command:

stagehand web-simple

The project scaffold will be created and you’ll be instructed to run pub get to update project dependencies. Run:

pub get

The dependencies for the project will be resolved and updated, allowing us to invoke the webdev tool to see our generated app in action.

Run the following command and point your browser to http://localhost:8080 once it says Serving ‘web’ on http://localhost:8080:

webdev serve

You should see something like this:

First Run

Making Some Changes

One thing you’ll quickly notice about working with Dart, is how fast changes are ready to be viewed. Let’s see this in action, by adding a bit of styling to our page.

First, open the file styles.css under the web folder. It should have the following content:

@import url(https://fonts.googleapis.com/css?family=Roboto);

html, body {
    width: 100%;
    height: 100%;
    margin: 0;
    padding: 0;
    font-family: 'Roboto', sans-serif;
}

#output {
    padding: 20px;
    text-align: center;
}

This was automatically created for us by the stagehand tool, when we instructed it to create a simple web project for us. Update the styles.css file by adding the following to #output:

font-weight: bold;
background: #BABBAA;

It should look like this:

#output {
    padding: 20px;
    text-align: center;
    font-weight: bold;
    background: #BABBAA;
}

Save the file, go to your browser and refresh the page. You should see something like this:

Updated CSS

The Dart tool webdev monitors the files in our project and, when you save changes, triggers a rebuild and redeploy automatically. This saves you a lot of time and effort and makes for a smoother developer experience. There are expections to this, but, for the most part, it works really well. Most of the time, all you need to do to see your changes, is refresh your browser.

If you look at the console output, you’ll see something like this, everytime you save changes to a file:

Updated Files

Updating the HTML

We’ve seen how easy it is to update the CSS, but what about the HTML? First, let’s take a look at the default HTML document stagehand created for us:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta name="scaffolded-by" content="https://github.com/google/stagehand">
        <title>dartweb</title>
        <link rel="stylesheet" href="styles.css">
        <link rel="icon" href="favicon.ico">
        <script defer src="main.dart.js"></script>
    </head>
    <body>
        <div id="output"></div>
    </body>
</html>

That looks pretty standard, right? The only thing that looks a bit funny is the src property of the <script> tag - main.dart.js. Notice the dart bit - we’ll get to that in the next section. For now, let’s focus on the HTML.

Since it’s just standard HTML, we can make changes. Add the following code to the page body, around the output div.

<header style="text-align: center;">
    <h1>A simple Dart web project</h1>
</header>

<div id="output"></div>

<footer>
    <p>For more information about Dart, please visit the <a href="https://www.dartlang.org/" target="_blank">offical website</a>.</p>
</footer>

Save the file and refresh your browser. It should look like this:

HTML Changes

Writing HTML for use with a Dart web project does not require any new skills or knowledge. You can use standard HTML tags, inline CSS as well as external style sheets.

Now, More About that Dart Bit in the JavaScript File

So where does Dart fit in with all of this then? We saw that the JavaScript include in the HTML had a dart in it, that was unusual. There’s also a main.dart file in our web folder, with the following content:

import 'dart:html';

void main() {
    querySelector('#output').text = 'Your Dart app is running.';
}

If you look at the ‘Your Dart app is running.’ sentence, you’ll see it’s what’s appearing on our HMTL page in the browser. The code selects the #output tag with the querySelector Dart method and changes the text to our message. So how does this work?

Dart compiles our source code to JavaScript, which runs in the browser. That means that you can write code that executes in the browser, in Dart, without having to know JavaScript!

We can make some changes to the Dart code, save the file and refresh the browser. See if you can change the main.dart code to say Hello, Dart!:

Updated Dart

Your main.dart file should look something like this now:

import 'dart:html';

void main() {
    querySelector('#output').text = 'Hello, Dart!';
}

Preparing to Do a Bit More

Before we start doing all kinds of things with Dart, let’s add a bit more content to our HTML page. You can use any images you prefer, but try to keep the identifier and style names the same, otherwise the code might not work.

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta name="scaffolded-by" content="https://github.com/google/stagehand">
        <title>dartweb</title>
        <link rel="stylesheet" href="styles.css">
        <link rel="icon" href="favicon.ico">
        <script defer src="main.dart.js"></script>
    </head>
    <body>
        <header style="text-align: center;">
            <h1>A simple Dart web project</h1>
        </header>
        <div id="output"></div>
        <div>
            <img src="tears_image.jpg" id="tears">
        </div>
        <div class="dogs">
            <ul>
                <li>roofus</li>
                <li>sally</li>
                <li>puddles</li>
                <li>abe</li>
                <li>nox</li>
                <li>charlie</li>
            </ul>
        </div>
        <footer>
            <p>For more information about Dart, please visit the <a href="https://www.dartlang.org/" target="_blank">offical website</a>.</p>
        </footer>
    </body>
</html>

The image I used:

TEARS Image

Save your changes and refresh your browser, you should see something like this:

Working Copy

Fixing the Image Alignment with Dart

Let’s say, for some reason, you’ll need to use Dart to fix the image alignment. While it’s probably more efficient to do it with static CSS, it gives us an opportunity to use Dart to interact with the DOM.

We want to select the image, then find the div it is contained in and change a property on that div. Then we also want to set some style properties on the image itself.

  1. Create a new method in the main.dart file:
void alignImageProperly() {
    querySelector('#tears')
        ..parent.style.setProperty("width", "100%")
        ..style.setProperty("display", "block")
        ..style.setProperty("margin-right", "auto")
        ..style.setProperty("margin-left", "auto");
}
  1. Make sure this method is called in main:
void main() {
    querySelector('#output').text = 'Hello, Dart!';
    alignImageProperly();
}
  1. Save and refresh!

Our expected output:

Image Centered

So what happened? Let’s look at the code in more detail. To make things easier, I’ll add line numbers:

1. void alignImageProperly() {
2.     querySelector('#tears')
3          ..parent.style.setProperty("width", "100%")
4.         ..style.setProperty("display", "block")
5.         ..style.setProperty("margin-right", "auto")
6.         ..style.setProperty("margin-left", "auto");
7. }
  1. We declare our new function, with the name alignImageProperly and a return type of void.
  2. Using the Dart method querySelector, we obtain a reference to the image with the ID of tears.
  3. Once we have this reference, we access the parent of our image and set the width property to 100%. Notice the use of the Dart cascade notation feature. This feature allows us to run the querySelector and keep a reference to the returned element. With this cascade operator, we can then call functions on this reference, without having to rerun the select or create a temporary variable.
  4. Now we set the display style property on our image itself.
  5. Set the margin-right on the image.
  6. Set the margin-left on the image.
  7. Close of our function body.

Adding Title to Our List of Names

Dart not only allows us to modify the styles of elements, but also makes it easy to add new elements to the DOM. Let’s add a heading to the list of names we have under our image.

Create a new function in your main.dart file.

addHeader() {
    var heading = HeadingElement.h3();
    heading.text = "Animal Names";
    querySelector('.dogs').children.insert(0, heading);
}

Make sure it gets called in main:

void main() {
    querySelector('#output').text = 'Hello, Dart!';
    alignImageProperly();
    addHeader();
}

Save and refresh, you should see something like this:

Name Title Added

So what happened in those three lines of code that make up our addHeader function? Well, we created a new HeadingElement, using the h3 named constructor feature of Dart and saved a reference in heading. Next, we changes the text of the heading element and finally, we inserted it as the first child of the div with the class of dogs.

Those Names Need Some Attention

Now that we’ve added a title to our list, we can look at fixing the list itself up a bit. Names should start with a capital letter, and the list looks sort of out of place.

Let’s fix the alignment up a bit, which is more of an excuse to add a CSS element to our stylesheet than good web development practice.

Create a function called addStyleElement with the following content:

var styleElement = new StyleElement();
document.head.append(styleElement);
CssStyleSheet sheet = styleElement.sheet;

final rule = '.dogs { width: 150px; padding: 20px; margin-left: auto; margin-right: auto; display: block; }';
sheet.insertRule(rule, 0);  

Make sure it gets called in the main function:

void main() {
    querySelector('#output').text = 'Hello, Dart!';
    alignImageProperly();
    addHeader();
    addStyleElement();
}

Save and refresh your browser and the list should look better!

List Aligned

Here’s what your addStyleElement function should look like, as a reference:

addStyleElement() {
    var styleElement = new StyleElement();
    document.head.append(styleElement);
    CssStyleSheet sheet = styleElement.sheet;

    final rule = '.dogs { width: 150px; padding: 20px; margin-left: auto; margin-right: auto; display: block; }';
    sheet.insertRule(rule, 0);  
}

Now, let’s do something about the fact that the names don’t start with a capital letter. Using Dart, we can get a reference to the list and go through the names, fixing the case issue for each item.

First, create a new function called fixCase with the following content:

fixCase() {
    var listElementChildren = querySelector('.dogs').children[1].children;

    listElementChildren.asMap().forEach((idx, value) {
        listElementChildren[idx].text = "${value.text[0].toUpperCase()}${value.text.substring(1)}";
    });
}

Make sure it gets called in the main function. Save your changes and refresh your browser. You should see something like this:

Case Fixed

Quite a lot happened in that short bit of code, so let’s look at it a little closer.

We first create a reference to the children of the second child of the <div> that contains our list of names. The title we added previously, is the 0th, or first child of the element. The second child is the list of names and it is it’s child elements we want to get to, as their case needs to be fixed.

Once we have the reference, we create a map, which allows us to fix each element in place, using the Dart string interpolation syntax. Since the reference we obtained is directly linked to the DOM, the values of the list are changed immediately, without further intervention required.

Fix the Dreadful Page Title

Update the page title in the main function to say something more appropriate. You can put in anything you want, I changed mine to:

New Title

The footer looks a little off, let’s fix the alignment. Create a new function called alignFooter with the following content:

alignFooter() {
    querySelectorAll('footer').first.style.setProperty("text-align", "center");
}

Make sure it gets called in main, then save and refresh your browser. The footer should now be properly aligned:

Footer Aligned

This time, we used the querySelectorAll, a very handy function that will select ALL the elements that fit the criteria. In our case, we have to use the first keyword to give us the first element in the list, because we know that we have only one footer on the page. From there, it’s the same procedure as before to set the style property to align the footer content.

Fix a Spelling Mistake in a Chunk of Text

Did you notice that official is spelled wrong in the footer? How can we find and fix it?

Let’s create a function called fixSpelling and call it from main:

fixSpelling() {
    var p = querySelector('footer').querySelector('p');
    p.innerHtml = p.innerHtml.replaceFirst('offical', 'official');
}

Save and refresh and you should see the following output:

Spelling Fixed

The spelling is fixed! But… Our link has disappeared? That’s not very useful. Dart was built with security in mind, and will, by default, disallow things like a href to be added to the node tree dynamically.

We’ll need to make some code changes here:

fixSpelling() {
    var p = querySelector('footer').querySelector('p');
    p.setInnerHtml(p.innerHtml.replaceFirst('offical', 'official'), treeSanitizer: AllowAllTreeSanitizer());
}

class AllowAllTreeSanitizer implements NodeTreeSanitizer {
    void sanitizeTree(Node node) {}
}

In our scenario, we want to allow all changes to happen to our website, as we are in control of the code and know what’s going to happen. We can then override the internal Dart sanitizer with our own, blank implementation that will allow anything to be added to the DOM.

This also happens to be an example of named parameters in Dart. Did you notice the treeSanitizer: paramenter name? Dart allows you to specify which parameter you are assigning a value to, which is really handy when you are working with optional parameters and don’t want to affect any others, even though the function can accept other parameters as well.

The output of these code changes should be:

Fixed Link

Build Our Web Application

Up till now, we’ve worked with webdev as our compile-and-deploy-as-we-make-changes server, and that’s great for development. However, it’s not really a good way to deploy our web application. Before we can deploy our app, we need to build it and create the deployment artifacts we’ll need to serve up via our web server.

Stop the webdev serve by pressing Ctrl-C (Break). The webdev tool can also be used to generate these artifacts. To build your project, execute the following command in the root folder of your project:

webdev build

Amongst the output, you’ll see a line like this:

[INFO] Creating merged output dir 'build' completed, took 147ms

If you examine the newly created build folder, you’ll see that webdev has created the deployment artifacts we need:

Build Files

Deploy Our Web Application

Deploying our web application is a very easy exercise. The webdev tool has already created all the deployment artifacts that we need, and we only need to upload this to our web server. We can remove the files we do not need to deploy to the server, like the hidden config files and the packages folder.

Deployment Artifacts:

Deployment Artifacts

Copy or upload these files to your prefered web server and your app is live! A simple way of doing this, is using Github Pages. Pages works great for web apps that run in the browser and don’t need server-side code to execute.

I’ve deployed the app we built to a Github Page, you can view it live by clicking the link.

Conclusion

This was a short introduction to building a very basic web app with Dart. For me, the biggest win is that I can use the same language I build mobile apps with, to build the web consoles I need. I hope you learned something and that you’ll look further into Dart as a language.

There are of course web application frameworks available, for use with Dart as well. I’ll list some of them in the resources section below.

Resources

Ewald Horn

Ewald Horn

Ewald cut his programming teeth on GW BASIC back in the '90s and, while his coding style hasn't really improved much, managed to keep his passion for learning and tinkering alive. Being editor and language agnostic, he's been known to dabble in all manner of technologies in his quest to solve business challenges. When not busy making a mess of his kitchen, he can often be found sitting in front of his computer, muttering something about how he's sure this used to be easier back in the day.