[Dart Language Tour] Variables, Built-in types, Comments

Variables, Built-in types, Comments

Variables

Here’s an example of creating a variable and initializing it:

1
var name = 'Bob';

Variables store references. The variable called name contains a reference to a String object with a value of “Bob”.

The type of the name variable is inferred to be String, but you can change that type by specifying it. If an object isn’t restricted to a single type, specify the Object or dynamic type, following design guidelines.

1
2
3
4
5
6
7
8
9
dynamic name = 'Bob';

// refer to number type.
name = 1;

Object anotherName = 'Bob';

// refer to number type.
anotherName = 2;

Another option is to explicitly declare the type that would be inferred:

1
2
3
4
// type annotations
String name = 'Bob';

name = 1; // Error: A value of type 'int' can't be assigned to a variable of type

Note: This page follows the style guide recommendation of using var, rather than type annotations, for local variables.


Default value

Uninitialized variables have an initial value of null. Even variables with numeric types are initially null, because numbers—like everything else in Dart—are objects.

1
2
int lineCount;
assert(lineCount == null);

Note: Production code ignores the assert() call. During development, on the other hand, assert(condition) throws an exception if condition is false. For details, see Assert.


Final and const

If you never intend to change a variable, use final or const, either instead of var or in addition to a type. A final variable can be set only once; a const variable is a compile-time constant. (Const variables are implicitly final.) A final top-level or class variable is initialized the first time it’s used.


Note: Instance variables can be final but not const. Final instance variables must be initialized before the constructor body starts — at the variable declaration, by a constructor parameter, or in the constructor’s initializer list.


Here’s an example of creating and setting a final variable:

1
2
final name = 'Bob'; // Without a type annotation
final String nickname = 'Bobby';

You can’t change the value of a final variable:

1
name = 'Alice'; // Error: a final variable can only be set once.

Use const for variables that you want to be compile-time constants. If the const variable is at the class level, mark it static const. Where you declare the variable, set the value to a compile-time constant such as a number or string literal, a const variable, or the result of an arithmetic operation on constant numbers:

1
2
const bar = 1000000; // Unit of pressure (dynes/cm2)
const double atm = 1.01325 * bar; // Standard atmosphere

The const keyword isn’t just for declaring constant variables. You can also use it to create constant values, as well as to declare constructors that create constant values. Any variable can have a constant value.

1
2
3
var foo = const [];
final bar = const [];
const baz = []; // Equivalent to `const []`

You can omit const from the initializing expression of a const declaration, like for baz above. For details, see DON’T use const redundantly.

You can change the value of a non-final, non-const variable, even if it used to have a const value:

1
foo = [1, 2, 3]; // Was const []

You can’t change the value of a const variable:

1
baz = [42]; // Error: Constant variables can't be assigned a value.

You can define constants that use type checks and casts (is and as), collection if, and spread operators (... and ...?):

1
2
3
4
const Object i = 3; // Where i is a const Object with an int value...
const list = [i as int]; // Use a typecast.
const map = {if (i is int) i: "int"}; // Use is and collection if.
const set = {if (list is List<int>) ...list}; // ...and a spread.

Note: Although a final object cannot be modified, its fields can be changed. In comparison, a const object and its fields cannot be changed: they’re immutable.


For more information on using const to create constant values, see Lists, Maps, and Classes.

Built-in types

The Dart language has special support for the following types:

  • numbers

  • strings

  • booleans

  • lists (also known as arrays)

  • sets

  • maps

  • runes (for expressing Unicode characters in a string)

  • symbols

You can initialize an object of any of these special types using a literal. For example, 'this is a string' is a string literal, and true is a boolean literal.

Because every variable in Dart refers to an object—an instance of a class—you can usually use constructors to initialize variables. Some of the built-in types have their own constructors. For example, you can use the Map() constructor to create a map.

Numbers

Dart numbers come in two flavors:

int

Integer values no larger than 64 bits, depending on the platform. On the Dart VM, values can be from -263 to 263 - 1. Dart that’s compiled to JavaScript uses JavaScript numbers - https://stackoverflow.com/questions/2802957/number-of-bits-in-javascript-numbers/2803010#2803010, allowing values from -253 to 253 - 1.

double

64-bit (double-precision) floating-point numbers, as specified by the IEEE 754 standard.

Both int and double are subtypes of num. The num type includes basic operators such as +, -, /, and *, and is also where you’ll find abs(), ceil(), and floor(), among other methods. (Bitwise operators, such as >>, are defined in the int class.) If num and its subtypes don’t have what you’re looking for, the dart:math - https://api.dart.dev/stable/dart-math library might.

Integers are numbers without a decimal point. Here are some examples of defining integer literals:

1
2
var x = 1;
var hex = 0xDEADBEEF;

If a number includes a decimal, it is a double. Here are some examples of defining double literals:

1
2
3
4
5
var y = 1.1;
var exponents = 1.42e5;
Integer literals are automatically converted to doubles when necessary:

double z = 1; // Equivalent to double z = 1.0.

Here’s how you turn a string into a number, or vice versa:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// String -> int
var one = int.parse('1');
assert(one == 1);

// String -> double
var onePointOne = double.parse('1.1');
assert(onePointOne == 1.1);

// int -> String
String oneAsString = 1.toString();
assert(oneAsString == '1');

// double -> String
String piAsString = 3.14159.toStringAsFixed(2);
assert(piAsString == '3.14');

The int type specifies the traditional bitwise shift (<<, >>), AND (&), and OR (|) operators. For example:

1
2
3
assert((3 << 1) == 6); // 0011 << 1 == 0110
assert((3 >> 1) == 1); // 0011 >> 1 == 0001
assert((3 | 4) == 7); // 0011 | 0100 == 0111

Literal numbers are compile-time constants. Many arithmetic expressions are also compile-time constants, as long as their operands are compile-time constants that evaluate to numbers.

1
2
3
const msPerSecond = 1000;
const secondsUntilRetry = 5;
const msUntilRetry = secondsUntilRetry * msPerSecond;

Strings

A Dart string is a sequence of UTF-16 code units. You can use either single or double quotes to create a string:

1
2
3
4
var s1 = 'Single quotes work well for string literals.';
var s2 = "Double quotes work just as well.";
var s3 = 'It\'s easy to escape the string delimiter.';
var s4 = "It's even easier to use the other delimiter.";

You can put the value of an expression inside a string by using ${expression}. If the expression is an identifier, you can skip the {}. To get the string corresponding to an object, Dart calls the object’s toString() method.

1
2
3
4
5
6
7
8
9
var s = 'string interpolation';

assert('Dart has $s, which is very handy.' ==
'Dart has string interpolation, ' +
'which is very handy.');
assert('That deserves all caps. ' +
'${s.toUpperCase()} is very handy!' ==
'That deserves all caps. ' +
'STRING INTERPOLATION is very handy!');

Note: The == operator tests whether two objects are equivalent. Two strings are equivalent if they contain the same sequence of code units.


You can concatenate strings using adjacent string literals or the + operator:

1
2
3
4
5
6
7
8
9
var s1 = 'String '
'concatenation'
" works even over line breaks.";
assert(s1 ==
'String concatenation works even over '
'line breaks.');

var s2 = 'The + operator ' + 'works, as well.';
assert(s2 == 'The + operator works, as well.');

Another way to create a multi-line string: use a triple quote with either single or double quotation marks:

1
2
3
4
5
6
7
var s1 = '''
You can create
multi-line strings like this one.
''';

var s2 = """This is also a
multi-line string.""";

You can create a “raw” string by prefixing it with r:

1
var s = r'In a raw string, not even \n gets special treatment.';

See Runes and grapheme clusters - https://dart.dev/guides/language/language-tour#characters for details on how to express Unicode characters in a string.

Literal strings are compile-time constants, as long as any interpolated expression is a compile-time constant that evaluates to null or a numeric, string, or boolean value.

1
2
3
4
5
6
7
8
9
10
11
12
13
// These work in a const string.
const aConstNum = 0;
const aConstBool = true;
const aConstString = 'a constant string';

// These do NOT work in a const string.
var aNum = 0;
var aBool = true;
var aString = 'a string';
const aConstList = [1, 2, 3];

const validConstString = '$aConstNum $aConstBool $aConstString';
// const invalidConstString = '$aNum $aBool $aString $aConstList';

For more information on using strings, see Strings and regular expressions - https://dart.dev/guides/libraries/library-tour#strings-and-regular-expressions.

Booleans

To represent boolean values, Dart has a type named bool. Only two objects have type bool: the boolean literals true and false, which are both compile-time constants.

Dart’s type safety means that you can’t use code like if (nonbooleanValue) or assert (nonbooleanValue). Instead, explicitly check for values, like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Check for an empty string.
var fullName = '';
assert(fullName.isEmpty);

// Check for zero.
var hitPoints = 0;
assert(hitPoints <= 0);

// Check for null.
var unicorn;
assert(unicorn == null);

// Check for NaN.
var iMeantToDoThis = 0 / 0;
assert(iMeantToDoThis.isNaN);

Lists

Perhaps the most common collection in nearly every programming language is the array, or ordered group of objects. In Dart, arrays are List - https://api.dart.dev/stable/dart-core/List-class.html objects, so most people just call them lists.

Dart list literals look like JavaScript array literals. Here’s a simple Dart list:

1
var list = [1, 2, 3];

Note: Dart infers that list has type List<int>. If you try to add non-integer objects to this list, the analyzer or runtime raises an error. For more information, read about type inference - https://dart.dev/guides/language/type-system#type-inference.


You can add a comma after the last item in a Dart collection literal. This trailing comma doesn’t affect the collection, but it can help prevent copy-paste errors.

1
2
3
4
5
var list = [
'Car',
'Boat',
'Plane',
];

Lists use zero-based indexing, where 0 is the index of the first value and list.length - 1 is the index of the last value. You can get a list’s length and refer to list values just as you would in JavaScript:

1
2
3
4
5
6
var list = [1, 2, 3];
assert(list.length == 3);
assert(list[1] == 2);

list[1] = 1;
assert(list[1] == 1);

To create a list that’s a compile-time constant, add const before the list literal:

1
2
var constantList = const [1, 2, 3];
// constantList[1] = 1; // This line will cause an error.

Dart 2.3 introduced the spread operator (...) and the null-aware spread operator (...?), which provide a concise way to insert multiple values into a collection.


For example, you can use the spread operator (...) to insert all the values of a list into another list:

1
2
3
var list = [1, 2, 3];
var list2 = [0, ...list];
assert(list2.length == 4);

If the expression to the right of the spread operator might be null, you can avoid exceptions by using a null-aware spread operator (...?):

1
2
3
var list;
var list2 = [0, ...?list];
assert(list2.length == 1);

For more details and examples of using the spread operator, see the spread operator proposal - https://github.com/dart-lang/language/blob/master/accepted/2.3/spread-collections/feature-specification.md.

Dart also offers collection if and collection for, which you can use to build collections using conditionals (if) and repetition (for).

Here’s an example of using collection if to create a list with three or four items in it:

1
2
3
4
5
6
var nav = [
'Home',
'Furniture',
'Plants',
if (promoActive) 'Outlet'
];

Here’s an example of using collection for to manipulate the items of a list before adding them to another list:

1
2
3
4
5
6
var listOfInts = [1, 2, 3];
var listOfStrings = [
'#0',
for (var i in listOfInts) '#$i'
];
assert(listOfStrings[1] == '#1');

For more details and examples of using collection if and for, see the control flow collections proposal - https://github.com/dart-lang/language/blob/master/accepted/2.3/control-flow-collections/feature-specification.md.

The List type has many handy methods for manipulating lists. For more information about lists, see Generics - https://dart.dev/guides/language/language-tour#generics and Collections - https://dart.dev/guides/libraries/library-tour#collections.

Sets

A set in Dart is an unordered collection of unique items. Dart support for sets is provided by set literals and the Set - https://api.dart.dev/stable/dart-core/Set-class.html type.

Here is a simple Dart set, created using a set literal:

1
var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};

Note: Dart infers that halogens has the type Set. If you try to add the wrong type of value to the set, the analyzer or runtime raises an error. For more information, read about type inference - https://dart.dev/guides/language/type-system#type-inference.


To create an empty set, use {} preceded by a type argument, or assign {} to a variable of type Set:

1
2
3
var names = <String>{};
// Set<String> names = {}; // This works, too.
// var names = {}; // Creates a map, not a set.

Set or map? The syntax for map literals is similar to that for set literals. Because map literals came first, {} defaults to the Map type. If you forget the type annotation on {} or the variable it’s assigned to, then Dart creates an object of type Map<dynamic, dynamic>.


Add items to an existing set using the add() or addAll() methods:

1
2
3
4
5
6
7
8
9
var elements = <String>{};
elements.add('fluorine');
elements.addAll(halogens);
Use .length to get the number of items in the set:

var elements = <String>{};
elements.add('fluorine');
elements.addAll(halogens);
assert(elements.length == 5);

To create a set that’s a compile-time constant, add const before the set literal:

1
2
3
4
5
6
7
8
final constantSet = const {
'fluorine',
'chlorine',
'bromine',
'iodine',
'astatine',
};
// constantSet.add('helium'); // This line will cause an error.

Sets support spread operators (... and ...?) and collection ifs and fors, just like lists do. For more information, see the list spread operator - https://dart.dev/guides/language/language-tour#spread-operator and list collection operator discussions.

For more information about sets, see Generics - https://dart.dev/guides/language/language-tour#generics and Sets - https://dart.dev/guides/libraries/library-tour#sets.

Maps

In general, a map is an object that associates keys and values. Both keys and values can be any type of object. Each key occurs only once, but you can use the same value multiple times. Dart support for maps is provided by map literals and the Map type.

Here are a couple of simple Dart maps, created using map literals:

1
2
3
4
5
6
7
8
9
10
11
12
var gifts = {
// Key: Value
'first': 'partridge',
'second': 'turtledoves',
'fifth': 'golden rings'
};

var nobleGases = {
2: 'helium',
10: 'neon',
18: 'argon',
};

Note: Dart infers that gifts has the type Map<String, String> and nobleGases has the type Map<int, String>. If you try to add the wrong type of value to either map, the analyzer or runtime raises an error. For more information, read about type inference.


You can create the same objects using a Map constructor:

1
2
3
4
5
6
7
8
9
var gifts = Map();
gifts['first'] = 'partridge';
gifts['second'] = 'turtledoves';
gifts['fifth'] = 'golden rings';

var nobleGases = Map();
nobleGases[2] = 'helium';
nobleGases[10] = 'neon';
nobleGases[18] = 'argon';

Note: If you come from a language like C# or Java, you might expect to see new Map() instead of just Map(). In Dart, the new keyword is optional. For details, see Using constructors - https://dart.dev/guides/language/language-tour#using-constructors.


Add a new key-value pair to an existing map just as you would in JavaScript:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var gifts = {'first': 'partridge'};
gifts['fourth'] = 'calling birds'; // Add a key-value pair
Retrieve a value from a map the same way you would in JavaScript:

var gifts = {'first': 'partridge'};
assert(gifts['first'] == 'partridge');
If you look for a key that isn’t in a map, you get a null in return:

var gifts = {'first': 'partridge'};
assert(gifts['fifth'] == null);
Use .length to get the number of key-value pairs in the map:

var gifts = {'first': 'partridge'};
gifts['fourth'] = 'calling birds';
assert(gifts.length == 2);
To create a map that’s a compile-time constant, add const before the map literal:

final constantMap = const {
2: 'helium',
10: 'neon',
18: 'argon',
};

// constantMap[2] = 'Helium'; // This line will cause an error.

Maps support spread operators (... and ...?) and collection if and for, just like lists do. For details and examples, see the spread operator proposal - https://github.com/dart-lang/language/blob/master/accepted/2.3/spread-collections/feature-specification.md and the control flow collections proposal - https://github.com/dart-lang/language/blob/master/accepted/2.3/control-flow-collections/feature-specification.md.

For more information about maps, see Generics - https://dart.dev/guides/language/language-tour#generics and Maps - https://dart.dev/guides/libraries/library-tour#maps.

Runes and grapheme clusters

In Dart, runes expose the Unicode code points of a string. You can use the characters package to view or manipulate user-perceived characters, also known as Unicode (extended) grapheme clusters.

Unicode defines a unique numeric value for each letter, digit, and symbol used in all of the world’s writing systems. Because a Dart string is a sequence of UTF-16 code units, expressing Unicode code points within a string requires special syntax. The usual way to express a Unicode code point is \uXXXX, where XXXX is a 4-digit hexadecimal value. For example, the heart character (♥) is \u2665. To specify more or less than 4 hex digits, place the value in curly brackets. For example, the laughing emoji (😆) is \u{1f606}.

If you need to read or write individual Unicode characters, use the characters getter defined on String by the characters package. The returned Characters object is the string as a sequence of grapheme clusters. Here’s an example of using the characters API:

1
2
3
4
5
6
import 'package:characters/characters.dart';
...
var hi = 'Hi 🇩🇰';
print(hi);
print('The end of the string: ${hi.substring(hi.length - 1)}');
print('The last character: ${hi.characters.last}\n');

The output, depending on your environment, looks something like this:

1
2
3
4
 dart bin/main.dart
Hi 🇩🇰
The end of the string: ???
The last character: 🇩🇰

For details on using the characters package to manipulate strings, see the example and API reference for the characters package.

Symbols

A Symbol object represents an operator or identifier declared in a Dart program. You might never need to use symbols, but they’re invaluable for APIs that refer to identifiers by name, because minification changes identifier names but not identifier symbols.

To get the symbol for an identifier, use a symbol literal, which is just # followed by the identifier:

1
2
#radix
#bar

Symbol literals are compile-time constants.

Comments

Dart supports single-line comments, multi-line comments, and documentation comments.

Single-line comments

A single-line comment begins with //. Everything between // and the end of line is ignored by the Dart compiler.

1
2
3
4
void main() {
// TODO: refactor into an AbstractLlamaGreetingFactory?
print('Welcome to my Llama farm!');
}

Multi-line comments

A multi-line comment begins with /* and ends with */. Everything between /* and */ is ignored by the Dart compiler (unless the comment is a documentation comment; see the next section). Multi-line comments can nest.

1
2
3
4
5
6
7
8
9
10
void main() {
/*
* This is a lot of work. Consider raising chickens.

Llama larry = Llama();
larry.feed();
larry.exercise();
larry.clean();
*/
}

Documentation comments

Documentation comments are multi-line or single-line comments that begin with /// or /**. Using /// on consecutive lines has the same effect as a multi-line doc comment.

Inside a documentation comment, the Dart compiler ignores all text unless it is enclosed in brackets. Using brackets, you can refer to classes, methods, fields, top-level variables, functions, and parameters. The names in brackets are resolved in the lexical scope of the documented program element.

Here is an example of documentation comments with references to other classes and arguments:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/// A domesticated South American camelid (Lama glama).
///
/// Andean cultures have used llamas as meat and pack
/// animals since pre-Hispanic times.
class Llama {
String name;

/// Feeds your llama [Food].
///
/// The typical llama eats one bale of hay per week.
void feed(Food food) {
// ...
}

/// Exercises your llama with an [activity] for
/// [timeLimit] minutes.
void exercise(Activity activity, int timeLimit) {
// ...
}
}

In the generated documentation, [Food] becomes a link to the API docs for the Food class.

To parse Dart code and generate HTML documentation, you can use the SDK’s documentation generation tool - https://github.com/dart-lang/dartdoc#dartdoc. For an example of generated documentation, see the Dart API documentation - https://api.dart.dev/stable. For advice on how to structure your comments, see Guidelines for Dart Doc Comments - https://dart.dev/guides/language/effective-dart/documentation.

References

[1] Language tour | Dart - https://dart.dev/guides/language/language-tour

[2] Effective Dart: Design | Dart - https://dart.dev/guides/language/effective-dart/design