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.
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.
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:
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
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:
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:
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:
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:
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.
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!:
Your main.dart file should look something like this now:
import 'dart:html';
void main() {
querySelector('#output').text = 'Hello, Dart!';
}
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:
Save your changes and refresh your browser, you should see something like this:
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.
void alignImageProperly() {
querySelector('#tears')
..parent.style.setProperty("width", "100%")
..style.setProperty("display", "block")
..style.setProperty("margin-right", "auto")
..style.setProperty("margin-left", "auto");
}
main
:void main() {
querySelector('#output').text = 'Hello, Dart!';
alignImageProperly();
}
Our expected output:
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. }
alignImageProperly
and a return type of void
.querySelector
, we obtain a reference to the image with the ID of tears
.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.display
style property on our image itself.margin-right
on the image.margin-left
on the image.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:
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
.
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!
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:
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.
Update the page title in the main
function to say something more appropriate. You can put in anything you want, I changed mine to:
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:
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.
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:
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:
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:
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:
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.
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.