Dart Programming
Introduction to Dart
About Dart
- Developed by Google for "Structured web apps". As a replacement for JavaScript.
- First mentioned at GOTO conference 2011 October 10-12.
- 1.0 released on 2013 November 14.
Where does Dart run?
- Command-line using Dart VM
- In a browser in Dart VM (Chromium + Dart = Dartium)
- Compiled to JavaScript in any browser. (dart2js)
Features
- class-based, object-oriented, single inheritance
- interfaces, abstract classes, reified generics
- Single threaded (asynchronous)
- optional typing / type annotations
Editors and IDEs
- Any text editor will do
- Eclipse-based "Dart Editor" comes with Dart, and Dartium
- Eclipse plugin
- JetBrains
- IntelliJ IDEA
- ...
- Spark (Chrome-App based, written in.dart" %}
- ...
- vim
Dart using vim
- vim
Syntax highlighting with Dart vim plugin
cd ~/
$ git clone https://github.com/dart-lang/dart-vim-plugin
ln -s ~/dart-vim-plugin/syntax/dart.vim ~/.vim/syntax/
And include the following lines in ~/.vimrc
" automatic Dart file type detection
au BufRead,BufNewFile *.dart set filetype=dart
Getting Help
Everything you need can be found on the main website of the Dart Language.
Hello World
- main
void main() {
print("Hello World");
}
dart hello_world.dart
Every Dart script must have a main subroutine that returns void, but we actually don't need to explicitly mention void.
Statements end with semi-colon: ;
print needs parentheses (as most functions do) and automatically appends a newline at the end of the output.
String interpolation
- var
-
- $
void main() {
var name = 'Foo';
print("Hello " + name + ", how are you?");
print("Hello $name, how are you?");
}
Adding numbers
-
- ${ }
Interpolation can also include embedded expressions. We just need to put them inside curly braces.
void main() {
var a = 23;
var b = 19;
print(a + b); // 42
var c = a + b;
print(c); // 42
print("The sum of $a and $b is ${ a + b }, right?");
}
Comments
- //
- /*
- */
Single line, whatever comes after // is a comment.
//
Multi-line, whatever is between a pair of /* */ is a comment.
/*
*/
Using string as a number
void main() {
var a = 23;
var b = 19;
print(a + b); // 42
var c = "23";
var d = "19";
print(c + d); // 2319
print(a + d);
}
{% embed include file="src/examples/dart-intro/using_string_as_number.out)
If both variables contain numbers, + will added them together as numbers. If they are both strings, + will concatenate them. If we try to add (+) two variables that hold different types, we will get a run-time exception.
Casting string to int
- int.parse
void main() {
var a = 23;
var b = 19;
print(a + b); // 42
var d = "19";
print( a + int.parse(d) ); // 42
}
Comparing number and string containing number
- toString
Strings can't equal to numbers even if the look the same. We first need to convert the string to an int and only then will they be equal.
void main() {
var x = 23;
var y = "23";
if (x == y)
print("string and int are not equal");
if (x == int.parse(y))
print("Converted string equals to int");
if (x.toString() == y)
print("Int converted to string also works");
}
if-statement
- if
- else
If there is only one statement inside the block of the if-statement, then there is no need to put curly braces. Just like in other C-like languages.
if (cond)
statement
if (cond) {
statement;
...
statement;
}
if (cond) {
...
} else {
...
}
else if
- else if
if (cond1) {
} else if (cond2) {
} else if (cond3) {
} else {
}
Logical operators
- !
- ||
- &&
switch case
- switch
- case
- break
Only works on numbers and strings. Last statement of each non-empty case must be one of: break, continue, return or throw.
main() {
var operator = '+';
var a = 23;
var b = 19;
var c;
switch(operator) {
case '+' :
c = a + b;
break;
case '-' :
c = a - b;
break;
default:
throw("Unknown operator $operator");
}
print(c);
}
Type checking
- int
- String
We can declare the types of variables, but that does not make any difference during normal run-time. We can still assign values of other types to the variable, and it sill only recognizes real type-mismatch in use at run-time.
void main() {
var a = 23;
print(a);
a = "hello";
print(a);
int b = 42;
print(b);
b = "abc";
print(b);
String c = "19";
int d = 23;
print(c + d);
}
dart type_checking.dart (production mode)
will throw exception on c+d
.
{% embed include file="src/examples/dart-intro/type_checking.out)
dart --checked type_checking.dart (checked mode)
will throw exception already on b = 'abc';
.
{% embed include file="src/examples/dart-intro/type_checking_checked.out)
IDE
The IDE will put little warning marks both of the above lines, and if we run it in the IDE we'll get an error at the same line where the --checked version go the exception.
Breaking on exception: type 'String' is not a subtype of type 'int' of 'b'.
Dart is an optionally typed and dynamic language. Variables can be
- annotated with static types
- untyped (aka. dynamic)
Types
- int
- double
- bool
- String
- final
- const
add(x, y) {
return x+y;
}
main() {
var name; // null
dynamic z = 1; // the same as var, not used
int age; // null
double o7; // null
bool isit; // null
String email; // null
name = 'Foo';
name = 'Bar';
const address = 'New York';
//address = "Moscow";
const double pi = 3 + 0.14;
final int answer = add(19, 23);
print(z); // 1
z++;
print(z); // 2
print(pi); // 3.14
print(answer); // 42
}
- Integers are arbitrary precision (but not in JS)
- Doubles are 64 bit
- Strings are UTF16
- Bools are true or false
Variables are either const, final, or ?? (normal?) Both const and final can be assigned only at the time of declaration, but final can be assigned during run-time while const can only get values that are either fixed or can be in-lined. Such as 1+2.
Operators on numbers
- ~/
main() {
var a = 8;
var b = 3;
print(a + b); // 11
print(a * b); // 24
print(a - b); // 5
print(a / b); // 2.66666666665
print(a ~/ b); // 2
a += 2;
print(a); // 10
a++;
print(a); // 11
a--;
print(a); // 10
}
Strings
- length
- """
- r''
- \n
main() {
var str = "Hello" "World";
print(str); // HelloWorld
str += " of Dart";
print(str); // HelloWorld of Dart
print(str.length); // 18
print(str[0]); // H
var here = """
Multi-line string
instead of Here documents.
""";
print(here);
var regular = 'regular strings \d \n\w';
var raw = r'raw strings \d \n\w for regex';
print(regular); // regular strings d
// w
print(raw); // raw strings \d \n\w for regex
}
String Methods
- contains
- indexOf
- replaceAll
- RegExp
main () {
var str = "The black cat climbed the green tree.";
print(str.contains('The')); // true
print(str.contains('The', 1)); // false
print(str.contains(new RegExp(r'dog|cat'))); // true
print(str.indexOf(new RegExp(r'dog|cat'), 2)); // 10
print(str.indexOf('dog')); // -1
print(str.replaceAll('climbed', 'jumped from'));
print(str);
}
StringBuffer
- StringBuffer
main() {
StringBuffer sb = new StringBuffer();
sb.write("Hello");
sb.writeAll(['space', 'and', 'more']);
print(sb);
print(sb.toString());
sb.clear();
}
Boolen values
- true
- false
true
and false
are boolean
values in Dart.
while - continue - break
- while
- break
- continue
main() {
int i = 0;
while (i < 3) {
i++;
print(i);
}
print('----');
while(true) {
i++;
if (i > 7) {
break;
}
if (i == 5) {
continue;
}
print(i);
}
}
do while loop
- do while
do {
} while (cond);
Functions
- function
- return
main() {
var z = add(2, 3);
print(z);
var q = multiply(2, 3);
print(q);
}
num add(num x, num y) {
return x+y;
}
multiply(x, y) => x*y;
Functions: Optional Positional Parameters
- []
- for
Wrapping the option in square brackets will make it optional. Its value will be null.
String prompt(String text, [int count]) {
if (count == null) {
count = 1;
}
for (int i = 0; i < count; i++) {
print(text);
}
}
void main() {
prompt("Your name:", 3);
prompt("Your Cat:");
}
Functions: Optional Named Parameters
- {}
If the parameters are wrapped in curly braces, it means they are still optional, but they are now named parameters. The name is now required when calling the function.
String prompt(String text, {int count}) {
if (count == null) {
count = 1;
}
for (int i = 0; i < count; i++) {
print(text);
}
}
void main() {
prompt("Your name:", count:3);
prompt("Your Cat:");
}
Functions: Optional Named Parameter with default value
String prompt(String text, {int count : 1}) {
for (int i = 0; i < count; i++) {
print(text);
}
}
void main() {
prompt("Your name:", count:3);
prompt("Your Cat:");
}
Functions: Optional Positional Parameter with default value
String prompt(String text, [int count = 1]) {
for (int i = 0; i < count; i++) {
print(text);
}
}
void main() {
prompt("Your name:", 3);
prompt("Your Cat:");
}
Optional Parameters
- [] - positional [int count = 1]
- {} - named {int count : 1}
In both cases the caller uses colon: f(positional, count : 42)
Functional Programming
- Functions are objects
- Function is an abstract type
hi(name) {
print("Hello $name");
}
var welcome = hi;
main() {
hi("Foo");
welcome("Bar");
}
Passing functions as parameters
doSomething(List values, Function func) {
for (var v in values) {
var r = func(v);
print("Input: $v Output: $r");
}
}
double_num(n) {
return 2*n;
}
main() {
doSomething([1, 2, 3], (n) => n*n);
doSomething([4, 5], double_num);
}
Specific function signatures
doSomething(List values, num func(num n)) {
for (var v in values) {
var r = func(v);
print("Input: $v Output: $r");
}
}
double_num(n) {
return 2*n;
}
main() {
doSomething([1, 2, 3], (n) => n*n);
doSomething([4, 5], double_num);
}
Lexical Scoping
Curly braces define scope. Both for regular variables and for functions. We cannot use the global version of f() if we declare f internally as well.
h() {
print("In external h");
}
f() {
print("In f");
g() {
print("In g");
}
h() {
print("In internal h");
}
g();
h();
}
main() {
f();
//g(); // Not declared
//f() {
// print("New f");
//}
h();
}
{% embed include file="src/examples/dart-intro/lexical_scope.out)
Closure
create_incrementor(num inc) {
incrementor(num) {
return num + inc;
}
return incrementor;
}
main() {
var inc17 = create_incrementor(17);
print(inc17(3)); // 20
var inc23 = create_incrementor(23);
print(inc23(19)); // 42
}
Complex data types
Iterables
Collections
-
Lists
-
Queues
-
Sets
-
Maps
Lists
- List
- shuffle
- last
- first
- add
- removeLast
- removeFirst
- removeAt
- forEach
Most other languages would probably call this an array. Well, except Python, where it is also called a list.
- Ordered
- Zero based
- Fast access via index
void main() {
List names = ["Foo", "Bar", "Qux"];
print(names.length); // 3
for (var i=0; i < names.length; i++) {
print(names[i]);
}
names.add('Zorg');
print(names); // [Foo, Bar, Qux, Zorg]
names.shuffle(); // this is random!
print(names); // [Zorg, Foo, Qux, Bar]
print(names.last); // Bar
print(names.first); //Zorg
print(names.removeLast()); //Bar
print(names); // [Zorg, Foo, Qux]
print(names.removeAt(0)); // Zorg
print(names); // [Foo, Qux]
names.forEach( (v) => print('Value is $v') );
for (var name in names) {
print(name);
}
// names[4] = 'Abc'; // RangeError: 4
// print(names[4]); // RangeError: 4
// List other = [];
// print(other.first); // No elements
}
where - filtering values
- where
- filter in Python
- grep in Perl
main() {
List languages = new List();
languages.add('Perl');
languages.add('Python');
languages.add('Dart');
print(languages); // [Perl, Python, Dart]
List short = languages.where((l) => l.length < 5).toList();
print(short); // [Perl, Dart]
}
any - checking if anything matches
- any
main() {
List languages = ['Perl', 'Python', 'Dart'];
print(languages); // [Perl, Python, Dart]
print(languages.any((l) => l.length < 5)); // true
}
map - making changes to each value
- map
main() {
List numbers = [1, 2, 3];
print(numbers); // [1, 2, 3]
var doubles = numbers.map((val) => 2 * val);
print(doubles); // (2, 4, 6)
var double_list = doubles.toList();
print(double_list); // [2, 4, 6]
}
Queue - FIFO
-
Queue
-
Ordered
-
Efficient add/remove from head/tail
-
No index access
import 'dart:collection';
main() {
Queue dentist = new Queue();
dentist.addLast("Foo");
dentist.addLast("Bar");
print(dentist.removeFirst()); // Foo
print(dentist.removeFirst()); // Bar
}
Stack - LIFO
-
Queue
-
Ordered
-
Efficient add/remove from head/tail
-
No index access
import 'dart:collection';
main() {
Queue dentist = new Queue();
dentist.addLast("Foo");
dentist.addLast("Bar");
print(dentist.removeLast()); // Bar
print(dentist.removeLast()); // Foo
}
The same Queue class can be also used to implement a Stack. Apparently adding at both ends and removing from both ends of a Queue class is fast and thus this class is optimal for both FIFO and LIFO data structures.
Sets
-
Set
-
intersection
-
difference
-
union
-
Unordered
-
Unique elements
//import 'dart:collection';
main() {
Set english = new Set();
english.add('door');
english.add('car');
english.add('door');
english.add('lunar');
english.add('era');
print(english.length); // 4
Set spanish = new Set();
spanish.addAll(['era', 'lunar', 'hola']);
print(spanish.length); // 3
print(spanish); // {era, lunar, hola}
print(english.intersection(spanish)); // {lunar, era}
print(english.difference(spanish)); // {door, car}
print(english.union(spanish)); // {door, car, lunar, era, hola}
}
Maps
-
map
-
hash
-
dictionary
-
associative array
-
Key/value pairs
-
Keys are unique and cannot be null
-
Access by key is O(1)
main() {
Map joe = {
"name" : "Joe",
"email" : "joe@somewhere.com"
};
print(joe); // {name: Joe, email: joe@somewhere.com}
joe.putIfAbsent('email', () => 'a@b.com');
print(joe); // {name: Joe, email: joe@somewhere.com}
joe.putIfAbsent('birthdate', () => new DateTime.now());
print(joe);
// {name: Joe, email: joe@somewhere.com, birthdate: 2014-02-25 20:53:04.548}
print(joe.containsKey('email')); // true
}
DateTime
- DateTime
main() {
var now = new DateTime.now();
print(now); // 2014-02-25 14:14:26.065
var then = DateTime.parse('2011-10-10 16:20:19');
print(then);
}
Read from Standard Input
- stdin.readLineSync
import 'dart:io';
void main() {
print("What's your name?");
var name = stdin.readLineSync();
print("How are you $name, today?");
}
Division by Zero
div(x, y) {
return x/y;
}
main() {
print(div(2, 1));
print(div(1, 0));
print(div(3, 1));
}
2.0
Infinity
3.0
Integer Division by Zero
div(x, y) {
return x ~/ y;
}
main() {
print(div(2, 1));
print(div(1, 0));
print(div(3, 1));
}
2
Breaking on exception: IntegerDivisionByZeroException
Catch exception
- try
- catch
div(x, y) {
return x ~/ y;
}
main() {
try {
print(div(2, 1));
print(div(1, 0));
print(div(3, 1));
} catch(exception, stackTrace) {
print(exception);
print(stackTrace);
}
print("still working");
}
{% embed include file="src/examples/dart-intro/catch_exception.out)
Catch specific exception
- StateError
- ArgumentError
- finally
div(x, y) {
return x ~/ y;
}
main() {
try {
print(div(2, 1));
print(div(1, 0));
print(div(3, 1));
} on StateError catch(exception, stackTrace) {
} on ArgumentError catch(e) {
} catch(exception, stackTrace) {
print(exception);
print(stackTrace);
} finally {
print("Runs no matter what");
}
print("still working");
}
Command line arguments
- argv
void main(List<String> arguments) {
print(arguments);
for (var a in arguments) {
print(a);
}
}
Timer
- dart:async
- Timer
import 'dart:async';
main() {
new Timer(new Duration(seconds: 1), () => print("timeout"));
print("end main");
}
Reading File
- dart:io
- dart:convert
- Platform
import 'dart:io'; // Platform, File
import 'dart:convert'; // UTF8, ASCII
main() {
var url = Platform.script;
print(url);
var filename = Platform.script.toFilePath();
print(filename);
var file = new File(filename);
var finished = file.readAsLines(encoding: UTF8);
finished.then( (text) => text.forEach((line) => print(line) ) );
//var finished = file.readAsString(encoding: UTF8);
//finished.then( (text) => print(text) );
}
Listing Directory
- Directory.current
- FileSystemEntity
import 'dart:io';
void main() {
var myDir = Directory.current;
print(myDir);
myDir.list()
.listen((FileSystemEntity entity) {
print(entity.path);
});
}
Random Numbers
- dart:math
- Random
import 'dart:math' show Random;
void main() {
var randomizer = new Random(); // can get a seed as a parameter
// Integer between 0 and 100 (0 can be 100 not)
var num = randomizer.nextInt(100);
print(num);
print(randomizer.nextBool());
var x = "1";
if (x)
print("x");
}
Classes
-
class
-
new
-
Top level class is called Object
-
Single inheritance
class Point {
double x;
double y;
move(dx, dy) {
x += dx;
y += dy;
}
Point(sx, sy) {
x = sx;
y = sy;
}
}
main() {
Point p = new Point(2.0, 3.0);
print(p); // Instance of 'Point'
print(p.x); // 2.0
p.x = 4.0;
print(p.x); // 4.0
}
Class with automatic getter and setter
class Person {
double age;
String name;
Person(this.name);
}
main() {
var p = new Person("Foo");
print(p.name);
p.age = 18.0;
print(p.age);
}
Create Getters and Setters
class Person {
DateTime birthday;
String name;
final int DAYS = 365;
Person(this.name, {this.birthday});
void set age(double years) {
var d = new DateTime.now();
var dur = new Duration(days: (DAYS*years).toInt());
d = d.subtract(dur);
birthday = d;
}
double get age {
var d = new DateTime.now();
return d.difference(birthday).inDays / DAYS;
}
double myage() {
var d = new DateTime.now();
return d.difference(birthday).inDays / DAYS;
}
}
main() {
var p = new Person("Foo");
print(p.name);
p.age = 18.0;
print(p.age);
print(p.myage()); // () required because it is not a real getter
var o = new Person("Bar", birthday: new DateTime.now());
print(o.name);
print(o.birthday);
}
Alternative constructor
class Person {
DateTime birthday;
String name;
final int DAYS = 365;
Person(this.name, {this.birthday});
Person.byAge(this.name, double years) {
var d = new DateTime.now();
var dur = new Duration(days: (DAYS*years).toInt());
d = d.subtract(dur);
birthday = d;
}
}
main() {
var p = new Person.byAge("Foo", 18.0);
print(p.name);
print(p.birthday);
var o = new Person("Bar", birthday: new DateTime.now());
print(o.name);
print(o.birthday);
}
Inheritance (extending a class)
- extends
class Point {
double x;
double y;
Point(this.x, this.y);
}
class Circle extends Point {
double r;
Circle(double x, double y, this.r) : super(x, y);
/*
toString() {
return "($x, $y, $r)";
}
*/
}
main() {
Point p = new Point(2.0, 3.0);
print(p); // Instance of 'Point'
print(p.x); // 2.0
print(p.y); // 3.0
var c = new Circle(4.0, 5.0, 2.5);
print(c); // Instance of 'Circle' (4.0, 5.0, 2.5)
print(c.x); // 4.0
print(c.y); // 5.0
print(c.r); // 2.5
}
Dart resources
- Home of Dart, Download Dart from there.
Ternary operator
- ?:
if (COND) {
X = A;
} else {
X = B;
}
X = COND ? A : B;
Number Guessing game
import 'dart:math' show Random;
import 'dart:io';
void main() {
var MAX = 200;
var randomizer = new Random();
var num = randomizer.nextInt(MAX);
print(num);
while (true) {
print("Please guess a number between 0 and $MAX:");
var guess = stdin.readLineSync();
if (guess == 'x')
break;
if (guess == num)
print("Success");
}
print("Goodbye");
}
Iterable
- Iterable
main() {
var numbers = new Iterable.generate(5, (i) => i);
print(numbers.length);
print(numbers);
for (var n in numbers) {
print(n);
}
}
Iterator
main () {
var names = ['Foo', 'Bar', 'Qux'];
for (var n in names) {
print(n);
}
print('----');
var it = names.iterator;
while (it.moveNext()) {
var n = it.current;
print(n);
}
}
Convert Dart date to JSON and back
- JSON
- dart::convert
import 'dart:convert';
var person = {
"name" : "Foo Bar",
"emails" : ["foo@bar.com", "foobar@gmail.com"],
"phone" : "1234",
};
main () {
print(person);
String person_in_json = JSON.encode(person);
print(person_in_json);
print(person_in_json.length); // 77
print(person.length); // 3
}
Examples
Placeholder for more examples.
import 'dart:io';
int add(x, y) {
return x + y;
}
final int z = add(19, 23);
const int q = 4;
main() {
print(z);
print(q);
/*
print(Platform.environment); // map of environment variables
print(Platform.environment["PATH"]);
print(Platform.operatingSystem);
print(Platform.executable);
*/
}