Eric Windmill
I’ve recently accepted a position with a company that has an interesting stack. They’re basically all in on Google (Go, Dart, Angular, Polymer and even Flutter). I’m a huge victim of shiny object syndrom, so I was quick to accept a job that would force me to learn all new languages and technologies.
I don’t know that I’m sold on wether anyone should learn Dart. JavaScript is such a massive force, that its certainly not going anywhere. However, Dart was created as an alternative to JavaScript, and its nice to have options.
From WikiPedia : ‘Dart is a general-purpose programming language originally developed by Google and later approved as a standard by Ecma. It is used to build web, server and mobile applications, and for Internet of Things devices.
Dart is an object-oriented, class defined, single inheritance language using a C-style syntax that transcompiles optionally into JavaScript. It supports interfaces, mixins, abstract classes, reified generics, optional typing, and a sound type system’
It seems that Dart can compile into JavaScript, but doesn’t have to. I’m not sure what the advantage of this, becuase JavaScript already can be run in browsers or server side.
Syntactically, Dart reminds me of TypeScript. It’s like JavaScript by more opinionated, and of course typed.
Like TypeScript, Dart seems to be a language that combines the useability of JavaScript with the opinions of slightly-lower-level and application languages like Java. I’d assume that a lot of Dart users fancy it over JS because its Object Oriented and supports all the Object Oriented Programming features.
Dart is currently in production at Google Fiber and Goodgle Ads Team to name a few.
The first resource I went through when looking into dart was Dart Academy Boot Camp. This resource is probably meant for a brand new developer who’s learning Dart as their first language. Nevertheless, there was some valuable insight into the basic syntax similarities and basic language functionality.
The second resouce I ran into is Dart by Example. Funny enough, the creator of this is a Sr. Engineer at my new company. This definitely get’s deeper into the language. I’m glad I started with the previous resource, but this is where the real learning happened.
There’s a nifty tool that’s similar to JSFiddle or CodePen, but for Dart called DartPad.
The format of this post is ‘constantly-updated’. I go out on the internet, I read and learn, and then I update the content inline. It isn’t sequential, but rather building up from basic to more advanced. By the end, I hope its a one-stop shop for someones first forray into Dart from JavaScript.
Getting to ‘Hello, World’ in Dart looks like this.
void main() {
print("Hello, World");
}
Let’s break that down.
void main()
main()
void
tells Dart that main()
will prodocue no output.void main() => print('Hello, World!');
var x;
establishes a variable called x to null
, just like in JS.var x = 5;
Dart variables are very similar to JS variables in ‘strict’ mode.
By default, variables are given the type ‘dynamic’.
void main() {
var x = 5;
x = 'Dart'
}
This is valid. But if you change the code to give x
a type, it wouldn’t work:
void main() {
int x = 5;
x = 'Dart';
}
This is invalid . In Dart, this will throw a warning, but not an error. It the program will still run. In fact, when dart runs, all variables are dynamic. The types are for people. They make the code cleaner, but they don’t effect the program. That’s pretty neat, actually.
Type | Description |
---|---|
int | whole number |
double | floating-point number |
num | int or double |
bool | Boolean |
String | string of 0 or more chars |
List | List of values (like array) |
Map | Map of key value-pairs |
dynamic / var | any type (detault) |
Operators in Dart are similar to JavaScript.
Operater | JS Equivelent | Description |
---|---|---|
+ | + | Addition or Concatenation |
- | - | Subtraction |
* | * | Multiplication |
/ | / | Division |
% | % | Modulo |
++ | ++ | Increment |
— | — | Decrement |
Operater | JS Equivelent | Description |
---|---|---|
! | ! | Not operator |
== | == | equal |
!= | != | Not equal |
< | < | less than |
<= | <= | less than or equal to |
> | > | greater than |
>= | >= | greater than or equal to |
Operater | JS Equivelent | Description |
---|---|---|
// | // | comment |
nu
This is something new to me. These operators were added to dart to help you deal with potentially null objects. I guess what these operators really provide are some syntatic sugar, because you can accomplish these goals quite easily anyway. Writing less code is always a win, so I’m on board.
The operator ??
is kind of short hand for a ternary. It gives you a ‘backup’ to return if the expression you’re working with resolves to null.
exp ?? otherExp
This is similar to
((x) => x == null ? otherExp : x)(exp)
If exp
is null
, return otherExp
, otherwise use exp
.
This is a very welcomed expression. Assign an expression a value only if its null
. It’s just like the lazy operator in ruby (and presumably many languages).
a ??= b
The above expression means ‘assign the value of b to a if a is null
, otherwise return a
as is.
The ?.
is used to call a method on an object is that object isn’t null
, otherwise it returns null. This is exciting to me.
object?.method()
That’s really neat. The purpose it serves is useful, and the syntax is readable. I’m into it.
And, you can chain calls.
object?.child?.method()
If the object or the child of the object are null, it returns null
. Cool!
Here is my very, very contrived example:
void main() {
List myArr = [1, 2, 3];
print(myArr);
myArr?.add(4);
print(myArr);
myArr = null;
myArr?.add(5);
print(myArr);
}
Which outputs:
[1, 2, 3]
[1, 2, 3, 4]
null
But if you remove that ?
from line 7, the second time that add()
is called, this is the output:
[1, 2, 3]
[1, 2, 3, 4]
Uncaught TypeError: C.JSNull_methods.add$1 is not a function
Nice!
Operater | JS Equivelent | Description |
---|---|---|
?? | - | null check before using an object |
---------- | --------------- | ------------ |
??= | - | null check before assigning an expression |
---------- | --------------- | ------------ |
?. | - | null check an object before using an method |
---------- | --------------- | ------------ |
Strings are also just like Javascript, with a couple cool extra features. I could be wrong, but I don’t believe some of these features are offered in JS.
establishing a string is exactly the same:
var string = "Hello"
Double and single quotes seem to behave the same way.
var quote = '"Hello, World!", said Orld'
And escaping is the same too:
var quote = '"Hello, World! Ain\'t life grand?", said Orld'
If you want to use variables, that’s easy:
void main() {
String name = 'Orld';
print(name + 'is hungry.');
}
But dart also gives us nice string interpolation. If you’re familiar with ES6, it’s only slightly different.
ES6 interpolation: "${name} is hungry"
Dart interpolation: "$name is hungry"
But funny enough, in order to interpolate logic inside a string, you use ${}
, just like JavaScript.
print(${4+5} = 5 + 4)
will print 9 = 5 + 4
print("Orld has ${contacts.length} friends")
will print however many contacts Orld has in his contacts
list.Its nice that Dart gives a nice shortcut, but I actually prefer the JS way. It seems easier to read. Maybe that’s just my bias as a JS developer.
Another escape trick is preceeding a string with ‘r’. This tells Dart that the following string will be considered raw.
so print(r"$name is hungry")
would print literally $name is hungry
. That’s a nice feature forsure.
The final to note is that Dart will automatically concat two adjacent strings.
void main() {
print("Hellow, " "Orld")
}
This will print Hellow, Orld
.
Boolean values are arguably not like Javascript. This is the first major difference I’ve seen in how you can use the language. In JavaScript, there are multiple values that can evaluate to true
. In fact, most values are considered ‘truthy’.
The ‘falsy’ values in JS are 0, false, null, ”, undefinied, and NaN. Everything else is ‘truthy’.
In Dart, true evaluates to true. Everything else is false.
Lists are basically arrays. The same JavaScript syntax that you see in arrays carries over to Dart.
List fruit = new List(3)
That sets up a fixed-length list, then you can access properties via bracket notation. fruit[0] = 'banana';
You can leave out the argument when establishing a new List
in order to establish a dynamic list.
Or, you can just create a list using literal syntax.
List fruit = ['banana', 'apple', 'tomato']
Lists are extended with the ‘iterable’ class, which gives them quite a few built in methods and properties. Some of the most important:
length
reversed
first
isEmpty
isNotEmpty
last
add
addAll
clear
indexOf
insert
remove
shuffle
sort
any
contains
every
firstWhere
forEach
join
map
reduce
toSet
toString
There are a lot of methods, and variations of many… Dart Lang List class API
Maps seem to be more or less the same, too.
Map fruit = new Map();
works, as do using braces for map literals.
var fruit = {
'banana': 'yellow',
'apple': 'red'
}
You can use for...in
loops to loop over Map in arbitrary order. More on loops soon.
The only two languages that I know well are Ruby and JavaScript. And both of those languages require you to implement classes like Stack and Queue manually. I’ve always been jealous of languages like Python that have them built in. Dart also has some built int, which is pretty awesome.
These are some of the basic structures you can use:
Sets are the same as JS, and equality is determinted with ==
Queues are Lists’ with Queue-like properties. You can’t lookup by index, but you can get the start and end elements quickly.
Constants are entirably immutable. They can be any object or value that can be determined at compile time. A good example of this can be found here. This article states that const three = 1 + 2
is a valid constant becasue it can be calculated at compile time, but const time = DateTime.now()
isn’t valid.
Const works recursively. This is unlike JavaScript. So all the values in a List must remain constant for the const
to be valid. In JavaScript, you can update the values in an Array, you just can’t change the variable itself to another type.
Const
values are signletons. They’re created once and reused, rather than recreated. This makes sense, because memory allocation. Why recreate something that you know is never going to change, when you could just continually reference that same object.
getConst() => const [1, 2];
void main() {
List a = getConst();
List b = getConst();
print (a === b); // true
This example was taken from dartlang.org.
Final objects are immutable. They’re technically single-assignments. Once they’re given a value, it can never be changed.
void main() {
final banana = "bread";
banana = "pancakes"; // runtime error; already assigned
// even Lists are completely immutable
final fruit = new List(3)
fruit[0] = 'banana' // runtime error; already assigned
}
Static functions and properties are available on the class, and not the instances of the class. In Ruby this is called ‘class methods’ (fittingly). JS uses composition rather than inheritance so the concept doesn’t exist.
(I’ve written more about Classes below.)
class Fruit {
static bool rotten = false
}
var apple = new Fruit();
void main() {
print(Fruit.rotten); // false
print(apple.rotten); // Uncaught TypeError: $.$get$apple(...).get$rotten is not a function
}
Conditionals are exactly the same in as they are in JS (and many other languages). You can conditionally execute blocks of code based on their condition’s boolean value:
if
else if
else
And, if you conditional code get’s out of hand, use a switch
statement.
The switch
statement seems to be, again, exactly like JavaScript. Like most languages that offer switch
, it’s essentially a cleaner way to write a long line of if else
conditionals. I rarely use switch
statements when writing in plain JS, but switch
is used in many frameworks (like React with Redux).
Here’s a basic Dart switch
statement:
void main() {
int chooseANum = 2;
switch(chooseANum) {
case 0:
print('zero');
break;
case 1:
print('one');
break;
case 2:
print('two');
break;
case 3:
print('three');
break;
default:
print('Thats not an integer!');
}
}
This is the exact syntax for JS switch statements, down to the required ‘breaks’, the default exectuion block, etc.
This post assumes you understand JavaScript, so maybe this is overkill, but I think it’s important to explain why ‘break’ is an advantage to switch
statements.
Suppose you have two possible outcomes, but many ways to reach either outcome. Let’s say, for example, that you want to tell whether a number between 1-10 is even or odd. Using a switch statement without a break in every case allows you to ‘batch’ your possible outcomes to a single execution block.
A very contrived example in an if-statement:
void main() {
void evenOrOdd() {
int myNum = 2;
if (myNum == 2 || myNum == 4 || myNum == 6 || myNum == 8 || myNum == 10) {
print('Even!');
} else if (myNum == 1 || myNum == 3 || myNum == 5 || myNum == 7 || myNum == 9) {
print('Odd!');
} else {
print('pick a new number!');
}
}
}
This is okay, but those conditions in the if
and if else
blocks aren’t very pretty. They could be more readable:
void main() {
void evenOrOdd() {
myNum = 6;
switch (myNum) {
case 2:
case 4:
case 6:
case 8:
case 10:
print('Even!');
break;
case 1:
case 3:
case 5:
case 7:
case 9:
print ('Odd!');
break;
default:
print ('choose a different number!');
}
}
}
Again, this example is completely contrived, and this isn’t the easiest way to accomplish this by any means. But, it proves a point: The way the switch
statement works is by basically comparing the condition to EACH case, and if it matches, then it executes that code. It can and will run through and exectue all blocks of code where the condition matches. That’s what makes the break
necessary. Using break
in every case essentailly turns your switch
into a series of if
and if else
s. It ensures that only one condition (the first met that evaluates to true) will run. This is also why no break is requires under default, because its the last condition. switch
runs (as you’d expect) from top to bottom.
The weird thing about switch
in Dart, is that it doesn’t allow you to work with operators
. As in, you couldn’t say case myNum % 2 == 0
, or a better example… the following is an invalid way to test a range numbers:
void main() {
int myNum = 9;
switch (myNum)
case myNum < 10: // Not valid!!!
print('less than 10');
break;
...
In that case, you’d want to use an if statement:
void main() {
int myNum = 9
if (myNum < 10) { // this works
print('less than 10');
...
Dart even uses the exact JS ternary syntax. Woop.
var bullseye = true
var gameOver = bullseye ? 'Game over, you win!' : 'throw again'
print($gameOver)
void main() {
int i = 0;
while (i <= 10) {
print(i);
i++;
}
}
Then there’s the do-while
loop:
A do while loop in any language simply tests the condition at the end of the loop iteration rather than the beginning. In laymans terms, the do-while loop will execute once no matter what, then at the end of that iteration it will test the condition before running the next time.
void main() {
int i = 1
do {
print(i);
} while (i == 0);
}
This example is pretty contrived also, but the point is that it’ll print i even though the while condition is i == 0
. It’ll just exit at the first test, which is after the first exectuion block.
And finally, for
loops. Again, just like JavaScript.
void main() {
for (int i = 0; i < 10; i++) {
print(i)
}
}
This example simply prints out 0 through 9. Just like in JS and every other language, you need to be careful of off-by-one errors!
for-in
loops are the same as well. These loops allow you to iterate over anything that that extends the mixin Iterable
void main() {
list nums = [1,2,3]
for (var x in collection) {
print(x)
}
}
Functions are actually pretty different in Dart. Obviously, a function is a function no matter the language, but Dart is more opinionated, which means there are optionally more details to tackle.
A basic function looks exactly the same:
peanutButter () {
return 'crunchy'
}
var pbType = peanutButter();
Fat-arrows can explicitly return:
peanutButter() => 'crunchy';
Functions return null
if no return value is specified.
String peanutButter(String type) {
return type;
}
This is contrived, becuase all this function is doing is returning it’s argument, but the syntax is sound for the point. The first use of String
establishes what the type of the return value will be, while the second use of String
tells the functions to expect a string as an argument.
One of the biggest quirks of JavaScript is that is has no opinions about what arguments are required or what it expects or whatever. You can pass a function 0 arguments, even when it expects some, and it will fail silently. You can pass a function 50 arguments, and it will just use the the first however-many that its expecting.
Dart is fairly more sane. Arguments are either required or optional, but you have to be explicit in establishing which ones are required and which are optional.
String peanutButter(String type, [String brand]) {
String returnVal = 'My favorite pb is ';
if (brand != null ) {
returnVal + brand + ' ';
}
return returnVal + type;
}
var pb1 = peanutButter('crunchy') // My favorite pb is crunchy
var pb2 = peanutButter('crunchy', ['Jif']) // My favorite pb is Jif crunchy
That second paramenter is option, as denoted by the square brackets.
There’s a second way to establish optional parameters, and tahts with curly braces:
String peanutButter(String type, {String brand}) {
// ...
}
If you use the curly brackets, then when you call the function you call it like so:
var pb3 = peanutButter('crunchy', brand: 'Jif');
This is simply to make the code more readable.
In curly-bracket optional arguments, you can give default values. I love default values from ruby, so this makes me very happy.
String peanutButter(String type, {String brand: 'Jif'}) {
// ...
}
(This example is contrived, because you’d never want to assume that a users default favorite brand is ‘Jif’.)
Finally, you can check wether an optional parameter has been passed in (opposed to a default value set in the code) by preceeding the name with a ?
String peanutButter(String type, {brand: 'Jif'}) {
String returnVal = 'My favorite pb is ';
if (?brand) { // will run only if brand has been passed in specifically by the user.
returnVal + brand + ' ';
}
return returnVal + type;
}
Like JavaScript, Dart is lexically scoped. This is a huge relief for me, because its one of the things about JS that’s the most important to understand, the most difficult to learn correctly, and one of my favorite parts of using JS.
One of the ‘gotchas’ of JavaScript is the use of var
vs let
in a loop with setTimeout. It’s one of the classic ways to show what a block
scoped variable is. (This isn’t a JS tutorial, so check out a good example here)
The point of that article to keep in mind is that let
is a block-scoped variable. So each new iteration of a loop produces a new version of the variable.
Dart is the same way, as this is lexical scoping.
main() {
printInt(num) {
print(num)
}
for (int i = 0; i < 3; i++) {
printInt() => i;
}
}
// 0
// 1
// 2
Handling errors is fun in any language. It’s actually pretty simple in Dart. Almost all exceptions are Errors.
Dart has four types of exceptions to explore:
To signal an error or exceptions occured, use throw
.
driveCar() {
if (tankEmpty) {
throw new StateError('there's no gas!')
}
}
You can throw any non-null object as an exception! But, Dart gives us a bunch of built in objects like ‘StateError’. You can also extend Exception class to make specific exceptions.
The try block allows you to handle exceptions more gracefully. You can tackle forseeable problems before they occrur.
try {
car.start();
car.drive();
} catch(exception, stackTrace) {
print(exception);
print(stackTrace);
}
This is pretty standard stuff here. Like JS, if the exception is thrown on line 2 ( car.start()
), then drive()
is never called.
In a try
block, you can use on
to specify the type of exception you’re trying to catch.
try {
car.start(key);
car.drive();
} on StateError catch(exception) {
car.retryStart();
} on ArgumentError catch(exception) {
print('wrong key!')
} catch(exception) {
print(exception);
}
The finally clause makes sure that no matter what, something runs at the end.
try {
car.start(key);
car.drive();
// ...
} catch(exception) {
// ...
} finally {
car.exit();
}
Wether the car starts or not, you’re evenutally going to get out of the car and get on with your day.
Classes in Dart are just the classic OOP classical inheritance system. You have classes, from which you can make new objects. This is quite different from JavaScript, so it’s probably worth going into. (However, if you’ve used another OOP language, like Ruby, this’ll be old news.)
So, you have classes, which I like to think of as blueprints. They don’t do anything on their own. They simply create a blueprint from which future objects will be built. Think of it like a car in a factory. You’re building cars, but they aren’t constructed without plenty of planning and instructions.
class Car {
String make = 'Volkswagen';
int gasoline = 100;
// ...
driveCar (key) {
gasoline -= 10;
}
}
This is helpful because now every time you need a car, you can just type var myCar = new Car ()
, and it automatically has those features. It’s the definition of writing DRY code.
Then, you can get more specific:
class Jetta extends Car {
String color = 'black';
int topSpeed = 100;
}
This Jetta class allow you to create new Jettas that automicatlly get the members (properties and functions) of both the Jetta class and the Car class (because it ‘extends’) that class.
If you’ve ever developed in React, then this may look familiar. In fact, JS now supports classes (as of ES6), but they’re really just a wrapper on protoypes. There’s a big debate about prototypes vs classes, but thats another conversation for another day. If you’re writing in dart, you’re using classes, so learn to love them!
Dart is an OOP language. Everything is an object, and everything is an instance of a class. All classes decend from object
Dart is a single-inheritance language, so you can extend
from only one class.
Constructos are used much like they are in the ES6 implementation of Class, and much like constructors in all languages I’ve used. Syntactically, you only need to define a function on the class with the same name as the class.
class Fruit {
Fruit (type) {
this.type = type;
}
}
var banana = new Fruit('banana');
Dart also allows for named constructors. I’ve never seen named constructors in languages I use, and I have to say they’re pretty awesome. It’s almost like throwing a bit of composition into classical inheritance.
class Fruit {
bool rotten = false
Fruit.banana() {
this.type = 'banana';
}
Fruit.apple() {
this.type = 'apple';
}
}
var tastyFruit = new Fruit.apple();
void main () {
print(tastyFruit.type) // apple
print(tastyFruit.rotten) // false
}
Again, a contrived example, but this feature is awesome!
Mixins are another core idea in many OOP languages. Essentially, Mixins are used to to add members to classes without establishing a new class.
For example, let’s say you have a Fruit class, some fruits have thick outer skins or shells that must be cut before you can eat them. Other don’t. You wouldn’t want your apples and grapes to have a function called removeShell()
, because there is no shell. That should be reserved for Watermelons and Bananas.
class Fruit {
bool tasty = true;
eatFruit () {
// ... eat the fruit
}
}
class Shell {
removeShell() {
// ... remove the peel or shell or whatever
}
}
// no shell...
class Apple extends Fruit {
// ... apple stuff
}
// shell...
class Watermelon extents Fruit with Shell {
}
void main() {
var tastyTreat = new Watermelon();
print(tastyTreat.removeShell());
}
The import keyword is pretty similar to the ES6 import
. It’s used to pull in libraries.
First, you have to create the library.
Let’s say we have this file structure:
src | — myLib.dart | — app.dart
// myLib.dart
library myLib;
// .. classes and objects etc....
// app.dart
import 'src/mylib.dart';
That’s the basic way to import libraries, but you can also abstract those libraries out into smaller files. With component based architecture sweeping the JS framework world, that makes me happy.
Let’s say we have this:
src | — myLib | — myLib | — class1 | — class2 | — app.dart
// src/myLib/myLib
library myLib
part 'class1'
part 'class2'
// src/myLib/class1
part of mylib;
// src/myLib/class2
part of mylib;
Now, you can import myLib
and get all the members of class1 and class2 as well.
After spending so much time figuring out the most complicated aspect of JavaScript, I’m stoked to see that there are a lot of similarities in Dart.
First, there are Futures. These are basically exactly the same as JS promises. Unfortunately, the name Promise is way more intutive. But I digress…
void main() {
biteFruit() {
// bite fruit
}
// Futures are called with .then()
eatFruit
.then(swallowFruit())
.then(celebrate())
}
In this excrutiatingly forced example, swallowFruit
and celebrate
are just callbacks. You can definie a callback within the argument just as you could in JavaScript.
Also just like JavaScript, you can catch errors.
eatFruit.catchError(() => print('fruit rotten!'));
If you aren’t coming from JavaScript, and you’re unclear on whats going on here, here’s a primer I wrote for using Promises and Async/Await in Javascript.
If you’re up to the latest JS asynchronous API, then this will look familar. Again, this is just like async
and await
in JavaScript.
You establish that a function is async
and then as the function is running, it will pause each time it sees the await
keyword. The await keyword should be placed between a function that you need to complete before the next line of code. In JS, this is the easiest way to wrap your head around asynchronous code, because it reads the way its executed.
void main() async {
// the code will pause until the function after 'await' is complete.
var data = await grabDataFromDb();
// this will work how you'd want it to work.
print($data);
}
This still blows my mind, even though I use it regularly in JS. It’s amazing how readable and simple asynchronous programming is in these languages.
Dart expands on this though. And adds the ability to use await in for loops. Inside of an async
function, you can use await for (var) ...
and the loop will await until all asynchronous code in the block is run before starting the next iteration. So, you can use delays and make HTTP requests inside your for loop, and it’ll work how you’d want. That’s awesome.
Sign up for my mailing list to receive new articles, mainly about Dart and Flutter, and other programming technologies.