title | description | short-title |
---|---|---|
A Tour of the Dart Language |
A tour of all of the major Dart language features. |
Language Tour |
This page shows you how to use each major Dart feature, from variables and operators to classes and libraries, with the assumption that you already know how to program in another language.
To learn more about Dart's core libraries, see A Tour of the Dart Libraries. Whenever you want more details about a language feature, consult the Dart Language Specification.
The following code uses many of Dart’s most basic features:
{% prettify dart %} // Define a function. printInteger(int aNumber) { print('The number is $aNumber.'); // Print to console. }
// This is where the app starts executing. main() { var number = 42; // Declare and initialize a variable. printInteger(number); // Call a function. } {% endprettify %}
Here’s what this program uses that applies to all (or almost all) Dart apps:
// This is a comment.
: A single-line comment. Dart also supports multi-line and document comments. For details, see Comments.
int
: A type. Some of the other built-in types
are String
, List
, and bool
.
42
: A number literal. Number literals are a kind of compile-time constant.
print()
: A handy way to display output.
'...'
(or "..."
)
: A string literal.
$variableName
(or ${expression}
)
: String interpolation: including a variable or expression’s string equivalent inside of a string literal. For more information, see Strings.
main()
: The special, required, top-level function where app execution starts. For more information, see The main() function.
var
: A way to declare a variable without specifying its type.
As you learn about the Dart language, keep these facts and concepts in mind:
-
Everything you can place in a variable is an object, and every object is an instance of a class. Even numbers, functions, and
null
are objects. All objects inherit from the Object class. -
Although Dart is strongly typed, type annotations are optional because Dart can infer types. In the code above,
number
is inferred to be of typeint
. When you want to explicitly say that no type is expected, use the special typedynamic
. -
Dart supports generic types, like
List<int>
(a list of integers) orList<dynamic>
(a list of objects of any type). -
Dart supports top-level functions (such as
main()
), as well as functions tied to a class or object (static and instance methods, respectively). You can also create functions within functions (nested or local functions). -
Similarly, Dart supports top-level variables, as well as variables tied to a class or object (static and instance variables). Instance variables are sometimes known as fields or properties.
-
Unlike Java, Dart doesn’t have the keywords
public
,protected
, andprivate
. If an identifier starts with an underscore (_), it’s private to its library. For details, see Libraries and visibility. -
Identifiers can start with a letter or underscore (_), followed by any combination of those characters plus digits.
-
Dart has both expressions (which have runtime values) and statements (which don't). For example, the conditional expression
condition ? expr1 : expr2
has a value ofexpr1
orexpr2
. Compare that to an if-else statement, which has no value. A statement often contains one or more expressions, but an expression can't directly contain a statement. -
Dart tools can report two kinds of problems: warnings and errors. Warnings are just indications that your code might not work, but they don’t prevent your program from executing. Errors can be either compile-time or run-time. A compile-time error prevents the code from executing at all; a run-time error results in an exception being raised while the code executes.
The following table lists the words that the Dart language treats specially.
{% assign ckw = ' 1' %} {% assign bii = ' 2' %} {% assign lrw = ' 3' %} | abstract{{bii}} | dynamic{{bii}} | implements{{bii}} | show{{ckw}} | | as{{bii}} | else | import{{bii}} | static{{bii}} | | assert | enum | in | super | | async{{ckw}} | export{{bii}} | interface{{bii}} | switch | | await{{lrw}} | external{{bii}} | is | sync{{ckw}} | | break | extends | library{{bii}} | this | | case | factory{{bii}} | mixin{{bii}} | throw | | catch | false | new | true | | class | final | null | try | | const | finally | on{{ckw}} | typedef{{bii}}| | continue | for | operator{{bii}} | var | | covariant{{bii}} | Function{{bii}} | part{{bii}} | void | | default | get{{bii}} | rethrow | while | | deferred{{bii}} | hide{{ckw}} | return | with | | do | if | set{{bii}} | yield{{lrw}} | {:.table .table-striped .nowrap}
Avoid using these words as identifiers. However, if necessary, the keywords marked with superscripts can be identifiers:
-
Words with the superscript 1 are contextual keywords, which have meaning only in specific places. They're valid identifiers everywhere.
-
Words with the superscript 2 are built-in identifiers. To simplify the task of porting JavaScript code to Dart, these keywords are valid identifiers in most places, but they can't be used as class or type names, or as import prefixes.
-
Words with the superscript 3 are newer, limited reserved words related to the asynchrony support that was added after Dart's 1.0 release. You can't use
await
oryield
as an identifier in any function body marked withasync
,async*
, orsync*
.
All other words in the table are reserved words, which can't be identifiers.
Here’s an example of creating a variable and initializing it:
{% prettify dart %} var name = 'Bob'; {% endprettify %}
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.
{% comment %} [PENDING: check on Object vs. dynamic guidance.] {% endcomment %}
{% prettify dart %} dynamic name = 'Bob'; {% endprettify %}
Another option is to explicitly declare the type that would be inferred:
{% prettify dart %} String name = 'Bob'; {% endprettify %}
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.
{% prettify dart %} int lineCount; assert(lineCount == null); {% endprettify %}
assert(condition)
throws an exception unless *condition* is true. For details,
see [Assert](#assert).
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.
Here's an example of creating and setting a final variable:
{% prettify dart %} final name = 'Bob'; // Without a type annotation final String nickname = 'Bobby'; {% endprettify %}
You can't change the value of a final variable:
{:.fails-sa}
{% prettify dart %} name = 'Alice'; // Error: a final variable can only be set once. {% endprettify %}
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:
{% prettify dart %} const bar = 1000000; // Unit of pressure (dynes/cm2) const double atm = 1.01325 * bar; // Standard atmosphere {% endprettify %}
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.
{% prettify dart %}
var foo = const [];
final bar = const [];
const baz = []; // Equivalent to const []
{% endprettify %}
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:
{% prettify dart %} foo = [1, 2, 3]; // Was const [] {% endprettify %}
You can't change the value of a const variable:
{:.fails-sa}
{% prettify dart %} baz = [42]; // Error: Constant variables can't be assigned a value. {% endprettify %}
For more information on using const
to create constant values, see
Lists, Maps, and Classes.
The Dart language has special support for the following types:
- numbers
- strings
- booleans
- lists (also known as arrays)
- 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.
{% comment %} PENDING: add info about support for Iterable, Future, Stream? Those can't be initialized using literals, but they do have special support. {% endcomment %}
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.
Dart numbers come in two flavors:
: 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, allowing values from -253 to 253 - 1.
{% comment %} [PENDING: What about values on Android & iOS? The informal spec is at https://github.com/dart-lang/sdk/blob/master/docs/language/informal/int64.md. {% endcomment %}
: 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 library might.
Integers are numbers without a decimal point. Here are some examples of defining integer literals:
{% prettify dart %} int x = 1; int hex = 0xDEADBEEF; {% endprettify %}
If a number includes a decimal, it is a double. Here are some examples of defining double literals:
{% prettify dart %} double y = 1.1; double exponents = 1.42e5; {% endprettify %}
Here’s how you turn a string into a number, or vice versa:
{% prettify dart %} // 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'); {% endprettify %}
The int type specifies the traditional bitwise shift (<<, >>), AND (&), and OR (|) operators. For example:
{% prettify dart %} assert((3 << 1) == 6); // 0011 << 1 == 0110 assert((3 >> 1) == 1); // 0011 >> 1 == 0001 assert((3 | 4) == 7); // 0011 | 0100 == 0111 {% endprettify %}
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.
{% prettify dart %} const msPerSecond = 1000; const secondsUntilRetry = 5; const msUntilRetry = secondsUntilRetry * msPerSecond; {% endprettify %}
A Dart string is a sequence of UTF-16 code units. You can use either single or double quotes to create a string:
{% prettify dart %} 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."; {% endprettify %}
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.
{% prettify dart %} var s = 'string interpolation';
assert('Dart has
You can concatenate strings using adjacent string literals or the +
operator:
{% prettify dart %} 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.'); {% endprettify %}
Another way to create a multi-line string: use a triple quote with either single or double quotation marks:
{% prettify dart %} var s1 = ''' You can create multi-line strings like this one. ''';
var s2 = """This is also a multi-line string."""; {% endprettify %}
You can create a “raw” string by prefixing it with r
:
{% prettify dart %} var s = r'In a raw string, not even \n gets special treatment.'; {% endprettify %}
See Runes 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.
{% prettify dart %} // 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'; {% endprettify %}
For more information on using strings, see Strings and regular expressions.
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:
{% prettify dart %} // 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); {% endprettify %}
Perhaps the most common collection in nearly every programming language is the array, or ordered group of objects. In Dart, arrays are List objects, so most people just call them lists.
Dart list literals look like JavaScript array literals. Here’s a simple Dart list:
{% prettify dart %} var list = [1, 2, 3]; {% endprettify %}
**Note:** The analyzer infers that `list` has type `List`. 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.](/guides/language/sound-dart#type-inference)Lists use zero-based indexing, where 0 is the index of the first element
and list.length - 1
is the index of the last element. You can get a
list’s length and refer to list elements just as you would in
JavaScript:
{% prettify dart %} var list = [1, 2, 3]; assert(list.length == 3); assert(list[1] == 2);
list[1] = 1; assert(list[1] == 1); {% endprettify %}
To create a list that's a compile-time constant,
add const
before the list literal:
{% prettify dart %} var constantList = const [1, 2, 3]; // constantList[1] = 1; // Uncommenting this causes an error. {% endprettify %}
The List type has many handy methods for manipulating lists. For more information about lists, see Generics and Collections.
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:
{% prettify dart %} var gifts = { // Key: Value 'first': 'partridge', 'second': 'turtledoves', 'fifth': 'golden rings' };
var nobleGases = { 2: 'helium', 10: 'neon', 18: 'argon', }; {% endprettify %}
**Note:** The analyzer infers that `gifts` has the type `Map` and `nobleGases` has the type `Map`. 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.](/guides/language/sound-dart#type-inference)You can create the same objects using a Map constructor:
{% prettify dart %} 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'; {% endprettify %}
**Note:** You might expect to see `new Map()` instead of just `Map()`. As of Dart 2, the `new` keyword is optional. For details, see [Using constructors](#using-constructors).Add a new key-value pair to an existing map just as you would in JavaScript:
{% prettify dart %} var gifts = {'first': 'partridge'}; gifts['fourth'] = 'calling birds'; // Add a key-value pair {% endprettify %}
Retrieve a value from a map the same way you would in JavaScript:
{% prettify dart %} var gifts = {'first': 'partridge'}; assert(gifts['first'] == 'partridge'); {% endprettify %}
If you look for a key that isn’t in a map, you get a null in return:
{% prettify dart %} var gifts = {'first': 'partridge'}; assert(gifts['fifth'] == null); {% endprettify %}
Use .length
to get the number of key-value pairs in the map:
{% prettify dart %} var gifts = {'first': 'partridge'}; gifts['fourth'] = 'calling birds'; assert(gifts.length == 2); {% endprettify %}
To create a map that's a compile-time constant,
add const
before the map literal:
{% prettify dart %} final constantMap = const { 2: 'helium', 10: 'neon', 18: 'argon', };
// constantMap[2] = 'Helium'; // Uncommenting this causes an error. {% endprettify %}
For more information about maps, see Generics and Maps.
In Dart, runes are the UTF-32 code points of a string.
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 32-bit Unicode values 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{1f600}
.
The String
class has several properties you can use to extract rune information.
The codeUnitAt
and codeUnit
properties return 16-bit code
units. Use the runes
property to get the runes of a string.
The following example illustrates the relationship between runes, 16-bit code units, and 32-bit code points. Click the run button {% asset red-run.png alt="" %} to see runes in action.
{% comment %} https://gist.github.com/589bc5c95318696cefe5 https://dartpad.dartlang.org/589bc5c95318696cefe5 Unicode emoji: http://unicode.org/emoji/charts/full-emoji-list.html
{% prettify dart %} void main() { var clapping = '\u{1f44f}'; print(clapping); print(clapping.codeUnits); print(clapping.runes.toList());
Runes input = Runes('\u2665 \u{1f605} \u{1f60e} \u{1f47b} \u{1f596} \u{1f44d}'); print(String.fromCharCodes(input)); } {% endprettify %} {% endcomment %}
<iframe src="{{site.custom.dartpad.embed-inline-prefix}}?id=589bc5c95318696cefe5&verticalRatio=65" width="100%" height="333px" style="border: 1px solid #ccc;"> </iframe>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:
#radix
#bar
{% comment %} The code from the following excerpt isn't actually what is being shown in the page
{% prettify dart %} // MOVE TO library tour?
void main() { print(Function.apply(int.parse, ['11'])); print(Function.apply(int.parse, ['11'], {#radix: 16})); print(Function.apply(int.parse, ['11a'], {#onError: handleError})); print(Function.apply( int.parse, ['11a'], {#radix: 16, #onError: handleError})); }
int handleError(String source) { return 0; } {% endprettify %} {% endcomment %}
Symbol literals are compile-time constants.
Dart is a true object-oriented language, so even functions are objects and have a type, Function. This means that functions can be assigned to variables or passed as arguments to other functions. You can also call an instance of a Dart class as if it were a function. For details, see Callable classes.
Here’s an example of implementing a function:
{% prettify dart %} bool isNoble(int atomicNumber) { return _nobleGases[atomicNumber] != null; } {% endprettify %}
Although Effective Dart recommends type annotations for public APIs, the function still works if you omit the types:
{% prettify dart %} isNoble(atomicNumber) { return _nobleGases[atomicNumber] != null; } {% endprettify %}
For functions that contain just one expression, you can use a shorthand syntax:
{% prettify dart %} bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null; {% endprettify %}
The => expr
syntax is a shorthand for
{ return expr; }
. The =>
notation
is sometimes referred to as fat arrow syntax.
A function can have two types of parameters: required and optional. The
required parameters are listed first, followed by any optional
parameters. Named optional parameters can also be marked as @required
.
See the next section for details.
Optional parameters can be either positional or named, but not both.
When calling a function, you can specify named parameters using
paramName: value
. For example:
{% prettify dart %} enableFlags(bold: true, hidden: false); {% endprettify %}
When defining a function, use
{param1, param2, …}
to specify named parameters:
{% prettify dart %} /// Sets the [bold] and [hidden] flags ... void enableFlags({bool bold, bool hidden}) {...} {% endprettify %}
Flutter instance creation expressions can get complex, so widget constructors use named parameters exclusively. This makes instance creation expressions easier to read.
You can annotate a named parameter in any Dart code (not just Flutter) with @required to indicate that it is a required parameter. For example:
{% prettify dart %} const Scrollbar({Key key, [!@required!] Widget child}) {% endprettify %}
When a Scrollbar
is constructed, the analyzer reports an issue when the
child
argument is absent.
Required is defined in the meta package. Either import
package:meta/meta.dart
directly, or import another package that exports
meta
, such as Flutter's package:flutter/material.dart
.
Wrapping a set of function parameters in []
marks them as optional
positional parameters:
{% prettify dart %} String say(String from, String msg, [String device]) { var result = '$from says $msg'; if (device != null) { result = '$result with a $device'; } return result; } {% endprettify %}
Here’s an example of calling this function without the optional parameter:
{% prettify dart %} assert(say('Bob', 'Howdy') == 'Bob says Howdy'); {% endprettify %}
And here’s an example of calling this function with the third parameter:
{% prettify dart %} assert(say('Bob', 'Howdy', 'smoke signal') == 'Bob says Howdy with a smoke signal'); {% endprettify %}
Your function can use =
to define default values for both named and positional
parameters. The default values must be compile-time constants.
If no default value is provided, the default value is null
.
Here's an example of setting default values for named parameters:
{% prettify dart %} /// Sets the [bold] and [hidden] flags ... void enableFlags({bool bold = false, bool hidden = false}) {...}
// bold will be true; hidden will be false. enableFlags(bold: true); {% endprettify %}
{% comment %}
PENDING: I don't see evidence that we've dropped support for :
.
Update if/when we do. Issue #?
See defaultNamedParameter
in the language spec.
{% endcomment %}
The next example shows how to set default values for positional parameters:
{% prettify dart %} String say(String from, String msg, [String device = 'carrier pigeon', String mood]) { var result = '$from says $msg'; if (device != null) { result = '$result with a $device'; } if (mood != null) { result = '$result (in a $mood mood)'; } return result; }
assert(say('Bob', 'Howdy') == 'Bob says Howdy with a carrier pigeon'); {% endprettify %}
You can also pass lists or maps as default values.
The following example defines a function, doStuff()
,
that specifies a default list for the list
parameter and a default map for the gifts
parameter.
{% comment %}
The function is called three times with different values.
Click the run button {% asset red-run.png alt="" %}
to see list and map default values in action.
{% endcomment %}
{% prettify dart %} void doStuff( {List list = const [1, 2, 3], Map<String, String> gifts = const { 'first': 'paper', 'second': 'cotton', 'third': 'leather' }}) { print('list: $list'); print('gifts: $gifts'); } {% endprettify %}
{% comment %} https://gist.github.com/d988cfce0a54c6853799 https://dartpad.dartlang.org/d988cfce0a54c6853799 (The gist needs updating: see #189)
<iframe src="{{site.custom.dartpad.embed-inline-prefix}}?id=d988cfce0a54c6853799&verticalRatio=70" width="100%" height="450px" style="border: 1px solid #ccc;"> </iframe> {% endcomment %}Every app must have a top-level main()
function, which serves as the
entrypoint to the app. The main()
function returns void
and has an
optional List<String>
parameter for arguments.
Here's an example of the main()
function for a web app:
{% prettify dart %} void main() { querySelector('#sample_text_id') ..text = 'Click me!' ..onClick.listen(reverseText); } {% endprettify %}
Here's an example of the main()
function for a command-line app that
takes arguments:
{% prettify dart %} // Run the app like this: dart args.dart 1 test void main(List arguments) { print(arguments);
assert(arguments.length == 2); assert(int.parse(arguments[0]) == 1); assert(arguments[1] == 'test'); } {% endprettify %}
You can use the args library to define and parse command-line arguments.
You can pass a function as a parameter to another function. For example:
{% prettify dart %} void printElement(int element) { print(element); }
var list = [1, 2, 3];
// Pass printElement as a parameter. list.forEach(printElement); {% endprettify %}
You can also assign a function to a variable, such as:
{% prettify dart %} var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!'; assert(loudify('hello') == '!!! HELLO !!!'); {% endprettify %}
This example uses an anonymous function. More about those in the next section.
Most functions are named, such as main()
or printElement()
.
You can also create a nameless function
called an anonymous function, or sometimes a lambda or closure.
You might assign an anonymous function to a variable so that,
for example, you can add or remove it from a collection.
An anonymous function looks similar to a named function— zero or more parameters, separated by commas and optional type annotations, between parentheses.
The code block that follows contains the function's body:
([[Type] param1[, …]]) {
codeBlock;
};
The following example defines an anonymous function with an untyped parameter, item
.
The function, invoked for each item in the list,
prints a string that includes the value at the specified index.
{% prettify dart %} var list = ['apples', 'bananas', 'oranges']; list.forEach((item) { print('${list.indexOf(item)}: $item'); }); {% endprettify %}
Click the run button {% asset red-run.png alt="" %} to execute the code.
{% comment %} https://gist.github.com/chalin/5d70bc1889d055c7a18d35d77874af88 https://dartpad.dartlang.org/5d70bc1889d055c7a18d35d77874af88 {% endcomment %}
<iframe src="{{site.custom.dartpad.embed-inline-prefix}}?id=5d70bc1889d055c7a18d35d77874af88&verticalRatio=60" width="100%" height="250px" style="border: 1px solid #ccc;"> </iframe>If the function contains only one statement, you can shorten it using fat arrow notation. Paste the following line into DartPad and click run to verify that it is functionally equivalent.
{% prettify dart %} list.forEach( (item) => print('${list.indexOf(item)}: $item')); {% endprettify %}
Dart is a lexically scoped language, which means that the scope of variables is determined statically, simply by the layout of the code. You can “follow the curly braces outwards” to see if a variable is in scope.
Here is an example of nested functions with variables at each scope level:
{% prettify dart %} bool topLevel = true;
void main() { var insideMain = true;
void myFunction() { var insideFunction = true;
void nestedFunction() {
var insideNestedFunction = true;
assert(topLevel);
assert(insideMain);
assert(insideFunction);
assert(insideNestedFunction);
}
} } {% endprettify %}
Notice how nestedFunction()
can use variables from every level, all
the way up to the top level.
A closure is a function object that has access to variables in its lexical scope, even when the function is used outside of its original scope.
Functions can close over variables defined in surrounding scopes. In the
following example, makeAdder()
captures the variable addBy
. Wherever the
returned function goes, it remembers addBy
.
{% prettify dart %} /// Returns a function that adds [addBy] to the /// function's argument. Function makeAdder(num addBy) { return (num i) => addBy + i; }
void main() { // Create a function that adds 2. var add2 = makeAdder(2);
// Create a function that adds 4. var add4 = makeAdder(4);
assert(add2(3) == 5); assert(add4(3) == 7); } {% endprettify %}
Here's an example of testing top-level functions, static methods, and instance methods for equality:
{% prettify dart %} void foo() {} // A top-level function
class A { static void bar() {} // A static method void baz() {} // An instance method }
void main() { var x;
// Comparing top-level functions. x = foo; assert(foo == x);
// Comparing static methods. x = A.bar; assert(A.bar == x);
// Comparing instance methods. var v = A(); // Instance #1 of A var w = A(); // Instance #2 of A var y = w; x = w.baz;
// These closures refer to the same instance (#2), // so they're equal. assert(y.baz == x);
// These closures refer to different instances, // so they're unequal. assert(v.baz != w.baz); } {% endprettify %}
All functions return a value. If no return value is specified, the
statement return null;
is implicitly appended to the function body.
{% prettify dart %} foo() {}
assert(foo() == null); {% endprettify %}
Dart defines the operators shown in the following table. You can override many of these operators, as described in Overridable operators.
|--------------------------+------------------------------------------------|
Description | Operator |
---|---|
unary postfix | expr++ expr-- () [] . ?. |
unary prefix | -expr !expr ~expr ++expr --expr |
multiplicative | * / % ~/ |
additive | + - |
shift | << >> |
bitwise AND | & |
bitwise XOR | ^ |
bitwise OR | ` |
relational and type test | >= > <= < as is is! |
equality | == != |
logical AND | && |
logical OR | ` |
if null | ?? |
conditional | expr1 ? expr2 : expr3 |
cascade | .. |
assignment | = *= /= ~/= %= += -= <<= >>= &= ^= ` |
{:.table .table-striped} |
When you use operators, you create expressions. Here are some examples of operator expressions:
{% prettify dart %} a++ a + b a = b a == b c ? a : b a is T {% endprettify %}
In the operator table,
each operator has higher precedence than the operators in the rows
that follow it. For example, the multiplicative operator %
has higher
precedence than (and thus executes before) the equality operator ==
,
which has higher precedence than the logical AND operator &&
. That
precedence means that the following two lines of code execute the same
way:
{% prettify dart %} // Parentheses improve readability. if ((n % i == 0) && (d % i == 0)) ...
// Harder to read, but equivalent. if (n % i == 0 && d % i == 0) ... {% endprettify %}
Dart supports the usual arithmetic operators, as shown in the following table.
|-----------------------------+-------------------------------------------|
| Operator | Meaning |
|-----------------------------+-------------------------------------------|
| +
| Add
| –
| Subtract
| -expr
| Unary minus, also known as negation (reverse the sign of the expression)
| *
| Multiply
| /
| Divide
| ~/
| Divide, returning an integer result
| %
| Get the remainder of an integer division (modulo)
{:.table .table-striped}
Example:
{% prettify dart %} assert(2 + 3 == 5); assert(2 - 3 == -1); assert(2 * 3 == 6); assert(5 / 2 == 2.5); // Result is a double assert(5 ~/ 2 == 2); // Result is an int assert(5 % 2 == 1); // Remainder
assert('5/2 = ${5 ~/ 2} r ${5 % 2}' == '5/2 = 2 r 1'); {% endprettify %}
Dart also supports both prefix and postfix increment and decrement operators.
|-----------------------------+-------------------------------------------|
| Operator | Meaning |
|-----------------------------+-------------------------------------------|
| ++var
| var = var + 1
(expression value is var + 1
)
| var++
| var = var + 1
(expression value is var
)
| --var
| var = var – 1
(expression value is var – 1
)
| var--
| var = var – 1
(expression value is var
)
{:.table .table-striped}
Example:
{% prettify dart %} var a, b;
a = 0; b = ++a; // Increment a before b gets its value. assert(a == b); // 1 == 1
a = 0; b = a++; // Increment a AFTER b gets its value. assert(a != b); // 1 != 0
a = 0; b = --a; // Decrement a before b gets its value. assert(a == b); // -1 == -1
a = 0; b = a--; // Decrement a AFTER b gets its value. assert(a != b); // -1 != 0 {% endprettify %}
The following table lists the meanings of equality and relational operators.
|-----------+-------------------------------------------|
| Operator | Meaning |
|-----------+-------------------------------------------|
| ==
| Equal; see discussion below
| !=
| Not equal
| >
| Greater than
| <
| Less than
| >=
| Greater than or equal to
| <=
| Less than or equal to
{:.table .table-striped}
To test whether two objects x and y represent the same thing, use the
==
operator. (In the rare case where you need to know whether two
objects are the exact same object, use the identical()
function instead.) Here’s how the ==
operator works:
-
If x or y is null, return true if both are null, and false if only one is null.
-
Return the result of the method invocation
x.==(y)
. (That’s right, operators such as==
are methods that are invoked on their first operand. You can even override many operators, including==
, as you’ll see in Overridable operators.)
Here’s an example of using each of the equality and relational operators:
{% prettify dart %} assert(2 == 2); assert(2 != 3); assert(3 > 2); assert(2 < 3); assert(3 >= 3); assert(2 <= 3); {% endprettify %}
The as
, is
, and is!
operators are handy for checking types at
runtime.
|-----------+-------------------------------------------|
| Operator | Meaning |
|-----------+-------------------------------------------|
| as
| Typecast
| is
| True if the object has the specified type
| is!
| False if the object has the specified type
{:.table .table-striped}
The result of obj is T
is true if obj
implements the interface
specified by T
. For example, obj is Object
is always true.
Use the as
operator to cast an object to a particular type. In
general, you should use it as a shorthand for an is
test on an object
following by an expression using that object. For example, consider the
following code:
{% prettify dart %} if (emp is Person) { // Type check emp.firstName = 'Bob'; } {% endprettify %}
You can make the code shorter using the as
operator:
{% prettify dart %} (emp as Person).firstName = 'Bob'; {% endprettify %}
As you’ve already seen, you can assign values using the =
operator.
To assign only if the assigned-to variable is null,
use the ??=
operator.
{% prettify dart %} // Assign value to a a = value; // Assign value to b if b is null; otherwise, b stays the same b ??= value; {% endprettify %}
{% comment %}
https://gist.github.com/9de887c4daf76d39e524 https://dartpad.dartlang.org/9de887c4daf76d39e524
{% prettify dart %} void assignValues(int a, int b, int value) { print('Initially: a == $a, b == $b'); // Assign value to a a = value; // Assign value to b if b is null; otherwise, b stays the same b ??= value; print('After: a == $a, b == $b'); }
main() { assignValues(0, 0, 1); assignValues(null, null, 1); } {% endprettify %} {% endcomment %}
Compound assignment operators such as +=
combine
an operation with an assignment.
| =
| –=
| /=
| %=
| >>=
| ^=
| +=
| *=
| ~/=
| <<=
| &=
| |=
{:.table}
Here’s how compound assignment operators work:
|-----------+----------------------+-----------------------|
| | Compound assignment | Equivalent expression |
|-----------+----------------------+-----------------------|
|For an operator op: | a op= b
| a = a op b
|Example: |a += b
| a = a + b
{:.table}
The following example uses assignment and compound assignment operators:
{% prettify dart %} var a = 2; // Assign using = a *= 3; // Assign and multiply: a = a * 3 assert(a == 6); {% endprettify %}
You can invert or combine boolean expressions using the logical operators.
|-----------------------------+-------------------------------------------|
| Operator | Meaning |
|-----------------------------+-------------------------------------------|
| !expr
| inverts the following expression (changes false to true, and vice versa)
| ||
| logical OR
| &&
| logical AND
{:.table .table-striped}
Here’s an example of using the logical operators:
{% prettify dart %} if (!done && (col == 0 || col == 3)) { // ...Do something... } {% endprettify %}
You can manipulate the individual bits of numbers in Dart. Usually, you’d use these bitwise and shift operators with integers.
|-----------------------------+-------------------------------------------|
| Operator | Meaning |
|-----------------------------+-------------------------------------------|
| &
| AND
| |
| OR
| ^
| XOR
| ~expr
| Unary bitwise complement (0s become 1s; 1s become 0s)
| <<
| Shift left
| >>
| Shift right
{:.table .table-striped}
Here’s an example of using bitwise and shift operators:
{% prettify dart %} final value = 0x22; final bitmask = 0x0f;
assert((value & bitmask) == 0x02); // AND assert((value & ~bitmask) == 0x20); // AND NOT assert((value | bitmask) == 0x2f); // OR assert((value ^ bitmask) == 0x2d); // XOR assert((value << 4) == 0x220); // Shift left assert((value >> 4) == 0x02); // Shift right {% endprettify %}
Dart has two operators that let you concisely evaluate expressions that might otherwise require if-else statements:
condition ? expr1 : expr2
: If condition is true, evaluates expr1 (and returns its value);
otherwise, evaluates and returns the value of expr2.
expr1 ?? expr2
: If expr1 is non-null, returns its value;
otherwise, evaluates and returns the value of expr2.
When you need to assign a value
based on a boolean expression,
consider using ?:
.
{% prettify dart %}
var visibility = isPublic ? 'public' : 'private';
{% endprettify %}
If the boolean expression tests for null,
consider using ??
.
{% prettify dart %}
String playerName(String name) => name ?? 'Guest';
{% endprettify %}
The previous example could have been written at least two other ways,
but not as succinctly:
{% prettify dart %}
// Slightly longer version uses ?: operator.
String playerName(String name) => name != null ? name : 'Guest';
// Very long version uses if-else statement.
String playerName(String name) {
if (name != null) {
return name;
} else {
return 'Guest';
}
}
{% endprettify %}
Cascades (..
) allow you to make a sequence of operations
on the same object. In addition to function calls,
you can also access fields on that same object.
This often saves you the step of creating a temporary variable and
allows you to write more fluid code.
Consider the following code:
{% prettify dart %}
querySelector('#confirm') // Get an object.
..text = 'Confirm' // Use its members.
..classes.add('important')
..onClick.listen((e) => window.alert('Confirmed!'));
{% endprettify %}
The first method call, querySelector()
, returns a selector object.
The code that follows the cascade notation operates
on this selector object, ignoring any subsequent values that
might be returned.
The previous example is equivalent to:
{% prettify dart %}
var button = querySelector('#confirm');
button.text = 'Confirm';
button.classes.add('important');
button.onClick.listen((e) => window.alert('Confirmed!'));
{% endprettify %}
You can also nest your cascades. For example:
{% prettify dart %}
final addressBook = (AddressBookBuilder()
..name = 'jenny'
..email = 'jenny@example.com'
..phone = (PhoneNumberBuilder()
..number = '415-555-0100'
..label = 'home')
.build())
.build();
{% endprettify %}
Be careful to construct your cascade on a function that returns
an actual object. For example, the following code fails:
{% prettify dart %}
var sb = StringBuffer();
sb.write('foo')
..write('bar'); // Error: method 'write' isn't defined for 'void'.
{% endprettify %}
The sb.write()
call returns void,
and you can't construct a cascade on void
.
**Note:**
Strictly speaking,
the "double dot" notation for cascades is not an operator.
It's just part of the Dart syntax.
You've seen most of the remaining operators in other examples:
|----------+-------------------------------------------|
| Operator | Name | Meaning |
|-----------+------------------------------------------|
| ()
| Function application | Represents a function call
| []
| List access | Refers to the value at the specified index in the list
| .
| Member access | Refers to a property of an expression; example: foo.bar
selects property bar
from expression foo
| ?.
| Conditional member access | Like .
, but the leftmost operand can be null; example: foo?.bar
selects property bar
from expression foo
unless foo
is null (in which case the value of foo?.bar
is null)
{:.table .table-striped}
For more information about the .
, ?.
, and ..
operators, see
Classes.
You can control the flow of your Dart code using any of the following:
if
and else
for
loops
while
and do
-while
loops
break
and continue
switch
and case
assert
You can also affect the control flow using try-catch
and throw
, as
explained in Exceptions.
Dart supports if
statements with optional else
statements, as the
next sample shows. Also see conditional expressions.
{% prettify dart %}
if (isRaining()) {
you.bringRainCoat();
} else if (isSnowing()) {
you.wearJacket();
} else {
car.putTopDown();
}
{% endprettify %}
Unlike JavaScript, conditions must use boolean values, nothing else. See
Booleans for more information.
You can iterate with the standard for
loop. For example:
{% prettify dart %}
var message = StringBuffer('Dart is fun');
for (var i = 0; i < 5; i++) {
message.write('!');
}
{% endprettify %}
Closures inside of Dart’s for
loops capture the value of the index,
avoiding a common pitfall found in JavaScript. For example, consider:
{% prettify dart %}
var callbacks = [];
for (var i = 0; i < 2; i++) {
callbacks.add(() => print(i));
}
callbacks.forEach((c) => c());
{% endprettify %}
The output is 0
and then 1
, as expected. In contrast, the example
would print 2
and then 2
in JavaScript.
If the object that you are iterating over is an Iterable, you can use the
forEach() method. Using forEach()
is a good option if you don’t need to
know the current iteration counter:
{% prettify dart %}
candidates.forEach((candidate) => candidate.interview());
{% endprettify %}
Iterable classes such as List and Set also support the for-in
form of
iteration:
{% prettify dart %}
var collection = [0, 1, 2];
for (var x in collection) {
print(x); // 0 1 2
}
{% endprettify %}
A while
loop evaluates the condition before the loop:
{% prettify dart %}
while (!isDone()) {
doSomething();
}
{% endprettify %}
A do
-while
loop evaluates the condition after the loop:
{% prettify dart %}
do {
printLine();
} while (!atEndOfPage());
{% endprettify %}
Use break
to stop looping:
{% prettify dart %}
while (true) {
if (shutDownRequested()) break;
processIncomingRequests();
}
{% endprettify %}
Use continue
to skip to the next loop iteration:
{% prettify dart %}
for (int i = 0; i < candidates.length; i++) {
var candidate = candidates[i];
if (candidate.yearsExperience < 5) {
continue;
}
candidate.interview();
}
{% endprettify %}
You might write that example differently if you’re using an
Iterable such as a list or set:
{% prettify dart %}
candidates
.where((c) => c.yearsExperience >= 5)
.forEach((c) => c.interview());
{% endprettify %}
Switch statements in Dart compare integer, string, or compile-time
constants using ==
. The compared objects must all be instances of the
same class (and not of any of its subtypes), and the class must not
override ==
.
Enumerated types work well in switch
statements.
**Note:**
Switch statements in Dart are intended for limited circumstances,
such as in interpreters or scanners.
Each non-empty case
clause ends with a break
statement, as a rule.
Other valid ways to end a non-empty case
clause are a continue
,
throw
, or return
statement.
Use a default
clause to execute code when no case
clause matches:
{% prettify dart %}
var command = 'OPEN';
switch (command) {
case 'CLOSED':
executeClosed();
break;
case 'PENDING':
executePending();
break;
case 'APPROVED':
executeApproved();
break;
case 'DENIED':
executeDenied();
break;
case 'OPEN':
executeOpen();
break;
default:
executeUnknown();
}
{% endprettify %}
The following example omits the break
statement in a case
clause,
thus generating an error:
{% prettify dart %}
var command = 'OPEN';
switch (command) {
case 'OPEN':
executeOpen();
// ERROR: Missing break
case 'CLOSED':
executeClosed();
break;
}
{% endprettify %}
However, Dart does support empty case
clauses, allowing a form of
fall-through:
{% prettify dart %}
var command = 'CLOSED';
switch (command) {
case 'CLOSED': // Empty case falls through.
case 'NOW_CLOSED':
// Runs for both CLOSED and NOW_CLOSED.
executeNowClosed();
break;
}
{% endprettify %}
If you really want fall-through, you can use a continue
statement and
a label:
{% prettify dart %}
var command = 'CLOSED';
switch (command) {
case 'CLOSED':
executeClosed();
continue nowClosed;
// Continues executing at the nowClosed label.
nowClosed:
case 'NOW_CLOSED':
// Runs for both CLOSED and NOW_CLOSED.
executeNowClosed();
break;
}
{% endprettify %}
A case
clause can have local variables, which are visible only inside
the scope of that clause.
Use an assert
statement to disrupt normal execution if a boolean
condition is false. You can find examples of assert statements
throughout this tour. Here are some more:
{% prettify dart %}
// Make sure the variable has a non-null value.
assert(text != null);
// Make sure the value is less than 100.
assert(number < 100);
// Make sure this is an https URL.
assert(urlString.startsWith('https'));
{% endprettify %}
**Note:**
Assert statements have no effect in production code;
they're for development only.
Flutter enables asserts in [debug mode.][Flutter debug mode]
Development-only tools such as [dartdevc][]
typically support asserts by default.
Some tools, such as [dart][] and [dart2js,][dart2js]
support asserts through a command-line flag: `--enable-asserts`.
To attach a message to an assert,
add a string as the second argument.
{% prettify dart %}
assert(urlString.startsWith('https'),
'URL ($urlString) should start with "https".');
{% endprettify %}
The first argument to assert
can be any expression that
resolves to a boolean value. If the expression’s value
is true, the assertion succeeds and execution
continues. If it's false, the assertion fails and an exception (an
AssertionError) is thrown.
Your Dart code can throw and catch exceptions. Exceptions are errors
indicating that something unexpected happened. If the exception isn’t
caught, the isolate that raised the exception is suspended, and
typically the isolate and its program are terminated.
In contrast to Java, all of Dart’s exceptions are unchecked exceptions.
Methods do not declare which exceptions they might throw, and you are
not required to catch any exceptions.
Dart provides Exception and Error
types, as well as numerous predefined subtypes. You can, of course,
define your own exceptions. However, Dart programs can throw any
non-null object—not just Exception and Error objects—as an exception.
Here’s an example of throwing, or raising, an exception:
{% prettify dart %}
throw FormatException('Expected at least 1 section');
{% endprettify %}
You can also throw arbitrary objects:
{% prettify dart %}
throw 'Out of llamas!';
{% endprettify %}
**Note:** Production-quality code usually throws types that implement
[Error][] or [Exception][].
Because throwing an exception is an expression, you can throw exceptions
in => statements, as well as anywhere else that allows expressions:
{% prettify dart %}
void distanceTo(Point other) => throw UnimplementedError();
{% endprettify %}
Catching, or capturing, an exception stops the exception from
propagating (unless you rethrow the exception).
Catching an exception gives you a chance to handle it:
{% prettify dart %}
try {
breedMoreLlamas();
} on OutOfLlamasException {
buyMoreLlamas();
}
{% endprettify %}
To handle code that can throw more than one type of exception, you can
specify multiple catch clauses. The first catch clause that matches the
thrown object’s type handles the exception. If the catch clause does not
specify a type, that clause can handle any type of thrown object:
{% prettify dart %}
try {
breedMoreLlamas();
} on OutOfLlamasException {
// A specific exception
buyMoreLlamas();
} on Exception catch (e) {
// Anything else that is an exception
print('Unknown exception: $e');
} catch (e) {
// No specified type, handles all
print('Something really unknown: $e');
}
{% endprettify %}
As the preceding code shows, you can use either on
or catch
or both.
Use on
when you need to specify the exception type. Use catch
when
your exception handler needs the exception object.
You can specify one or two parameters to catch()
.
The first is the exception that was thrown,
and the second is the stack trace (a StackTrace object).
{% prettify dart %}
try {
// ···
} on Exception catch [!(e)!] {
print('Exception details:\n $e');
} catch [!(e, s)!] {
print('Exception details:\n $e');
print('Stack trace:\n $s');
}
{% endprettify %}
To partially handle an exception,
while allowing it to propagate,
use the rethrow
keyword.
{% prettify dart %}
void misbehave() {
try {
dynamic foo = true;
print(foo++); // Runtime error
} catch (e) {
print('misbehave() partially handled ${e.runtimeType}.');
[!rethrow;!] // Allow callers to see the exception.
}
}
void main() {
try {
misbehave();
} catch (e) {
print('main() finished handling ${e.runtimeType}.');
}
}
{% endprettify %}
To ensure that some code runs whether or not an exception is thrown, use
a finally
clause. If no catch
clause matches the exception, the
exception is propagated after the finally
clause runs:
{% prettify dart %}
try {
breedMoreLlamas();
} finally {
// Always clean up, even if an exception is thrown.
cleanLlamaStalls();
}
{% endprettify %}
The finally
clause runs after any matching catch
clauses:
{% prettify dart %}
try {
breedMoreLlamas();
} catch (e) {
print('Error: $e'); // Handle the exception first.
} finally {
cleanLlamaStalls(); // Then clean up.
}
{% endprettify %}
Learn more by reading the
Exceptions
section of the library tour.
Dart is an object-oriented language with classes and mixin-based
inheritance. Every object is an instance of a class, and all classes
descend from Object.
Mixin-based inheritance means that although every class (except for
Object) has exactly one superclass, a class body can be reused in
multiple class hierarchies.
Objects have members consisting of functions and data (methods and
instance variables, respectively). When you call a method, you invoke
it on an object: the method has access to that object’s functions and
data.
Use a dot (.
) to refer to an instance variable or method:
{% prettify dart %}
var p = Point(2, 2);
// Set the value of the instance variable y.
p.y = 3;
// Get the value of y.
assert(p.y == 3);
// Invoke distanceTo() on p.
num distance = p.distanceTo(Point(4, 4));
{% endprettify %}
Use ?.
instead of .
to avoid an exception
when the leftmost operand is null:
{% comment %}
https://dartpad.dartlang.org/0cb25997742ed5382e4a
https://gist.github.com/0cb25997742ed5382e4a
{% endcomment %}
{% prettify dart %}
// If p is non-null, set its y value to 4.
p?.y = 4;
{% endprettify %}
You can create an object using a constructor.
Constructor names can be either ClassName
or
ClassName.identifier
. For example,
the following code creates Point
objects using the
Point()
and Point.fromJson()
constructors:
{% prettify dart %}
var p1 = Point(2, 2);
var p2 = Point.fromJson({'x': 1, 'y': 2});
{% endprettify %}
The following code has the same effect, but
uses the optional new
keyword before the constructor name:
{% prettify dart %}
var p1 = new Point(2, 2);
var p2 = new Point.fromJson({'x': 1, 'y': 2});
{% endprettify %}
**Version note:** The `new` keyword became optional in Dart 2.
Some classes provide constant constructors.
To create a compile-time constant using a constant constructor,
put the const
keyword before the constructor name:
{% prettify dart %}
var p = const ImmutablePoint(2, 2);
{% endprettify %}
Constructing two identical compile-time constants results in a single,
canonical instance:
{% prettify dart %}
var a = const ImmutablePoint(1, 1);
var b = const ImmutablePoint(1, 1);
assert(identical(a, b)); // They are the same instance!
{% endprettify %}
Within a constant context, you can omit the const
before a constructor
or literal. For example, look at this code, which creates a const map:
{% prettify dart %}
// Lots of const keywords here.
const pointAndLine = const {
'point': const [const ImmutablePoint(0, 0)],
'line': const [const ImmutablePoint(1, 10), const ImmutablePoint(-2, 11)],
};
{% endprettify %}
You can omit all but the first use of the const
keyword:
{% prettify dart %}
// Only one const, which establishes the constant context.
const pointAndLine = {
'point': [ImmutablePoint(0, 0)],
'line': [ImmutablePoint(1, 10), ImmutablePoint(-2, 11)],
};
{% endprettify %}
If a constant constructor is outside of a constant context
and is invoked without const
,
it creates a non-constant object:
{% prettify dart %}
var a = const ImmutablePoint(1, 1); // Creates a constant
var b = ImmutablePoint(1, 1); // Does NOT create a constant
assert(!identical(a, b)); // NOT the same instance!
{% endprettify %}
**Version note:** The `const` keyword became optional
within a constant context in Dart 2.
To get an object's type at runtime,
you can use Object's runtimeType
property,
which returns a Type object.
{% prettify dart %}
print('The type of a is ${a.runtimeType}');
{% endprettify %}
Up to here, you've seen how to use classes.
The rest of this section shows how to implement classes.
Here’s how you declare instance variables:
{% prettify dart %}
class Point {
num x; // Declare instance variable x, initially null.
num y; // Declare y, initially null.
num z = 0; // Declare z, initially 0.
}
{% endprettify %}
All uninitialized instance variables have the value null
.
All instance variables generate an implicit getter method. Non-final
instance variables also generate an implicit setter method. For details,
see Getters and setters.
{% prettify dart %}
class Point {
num x;
num y;
}
void main() {
var point = Point();
point.x = 4; // Use the setter method for x.
assert(point.x == 4); // Use the getter method for x.
assert(point.y == null); // Values default to null.
}
{% endprettify %}
If you initialize an instance variable where it is declared (instead of
in a constructor or method), the value is set when the instance is
created, which is before the constructor and its initializer list
execute.
Declare a constructor by creating a function with the same name as its
class (plus, optionally, an additional identifier as described in
Named constructors).
The most common form of constructor, the generative constructor, creates
a new instance of a class:
{% prettify dart %}
class Point {
num x, y;
Point(num x, num y) {
// There's a better way to do this, stay tuned.
this.x = x;
this.y = y;
}
}
{% endprettify %}
The this
keyword refers to the current instance.
**Note:**
Use `this` only when there is a name conflict. Otherwise, Dart style
omits the `this`.
The pattern of assigning a constructor argument to an instance variable
is so common, Dart has syntactic sugar to make it easy:
{% prettify dart %}
class Point {
num x, y;
// Syntactic sugar for setting x and y
// before the constructor body runs.
Point(this.x, this.y);
}
{% endprettify %}
If you don’t declare a constructor, a default constructor is provided
for you. The default constructor has no arguments and invokes the
no-argument constructor in the superclass.
Subclasses don’t inherit constructors from their superclass. A subclass
that declares no constructors has only the default (no argument, no
name) constructor.
Use a named constructor to implement multiple constructors for a class
or to provide extra clarity:
{% prettify dart %}
class Point {
num x, y;
Point(this.x, this.y);
// Named constructor
[!Point.origin()!] {
x = 0;
y = 0;
}
}
{% endprettify %}
Remember that constructors are not inherited, which means that a
superclass’s named constructor is not inherited by a subclass. If you
want a subclass to be created with a named constructor defined in the
superclass, you must implement that constructor in the subclass.
By default, a constructor in a subclass calls the superclass’s unnamed,
no-argument constructor.
The superclass's constructor is called at the beginning of the
constructor body. If an initializer list
is also being used, it executes before the superclass is called.
In summary, the order of execution is as follows:
- initializer list
- superclass's no-arg constructor
- main class's no-arg constructor
If the superclass doesn’t have an unnamed, no-argument constructor,
then you must manually call one of the constructors in the
superclass. Specify the superclass constructor after a colon (:
), just
before the constructor body (if any).
In the following example, the constructor for the Employee class
calls the named constructor for its superclass, Person.
Click the run button {% asset red-run.png alt="" %} to execute the code.
{% comment %}
https://gist.github.com/Sfshaza/e57aa06401e6618d4eb8
https://dartpad.dartlang.org/e57aa06401e6618d4eb8
{% prettify dart %}
class Person {
String firstName;
Person.fromJson(Map data) {
print('in Person');
}
}
class Employee extends Person {
// Person does not have a default constructor;
// you must call super.fromJson(data).
Employee.fromJson(Map data) : super.fromJson(data) {
print('in Employee');
}
}
void main() {
var emp = Employee.fromJson({});
// Prints:
// in Person
// in Employee
if (emp is Person) {
// Type check
emp.firstName = 'Bob';
}
(emp as Person).firstName = 'Bob';
}
{% endprettify %}
{% endcomment %}
<iframe
src="{{site.custom.dartpad.embed-inline-prefix}}?id=e57aa06401e6618d4eb8&verticalRatio=80"
width="100%"
height="500px"
style="border: 1px solid #ccc;">
</iframe>
Because the arguments to the superclass constructor are evaluated before
invoking the constructor, an argument can be an expression such as a
function call:
{% prettify dart %}
class Employee extends Person {
Employee() : super.fromJson(getDefaultData());
// ···
}
{% endprettify %}
**Warning:**
Arguments to the superclass constructor do not have access to `this`.
For example, arguments can call static methods but not instance methods.
Besides invoking a superclass constructor, you can also initialize
instance variables before the constructor body runs. Separate
initializers with commas.
{% prettify dart %}
// Initializer list sets instance variables before
// the constructor body runs.
Point.fromJson(Map<String, num> json)
: x = json['x'],
y = json['y'] {
print('In Point.fromJson(): ($x, $y)');
}
{% endprettify %}
**Warning:**
The right-hand side of an initializer does not have access to `this`.
During development, you can validate inputs by using assert
in the
initializer list.
{% prettify dart %}
Point.withAssert(this.x, this.y) : [!assert(x >= 0)!] {
print('In Point.withAssert(): ($x, $y)');
}
{% endprettify %}
{% comment %}
[PENDING: the example could be better.
Note that DartPad doesn't support this yet?
dart-lang/sdk#30968
https://github.com/dart-lang/sdk/blob/master/docs/language/informal/assert-in-initializer-list.md]
{% endcomment %}
Initializer lists are handy when setting up final fields.
The following example initializes three final fields in an initializer list.
Click the run button {% asset red-run.png alt="" %} to execute the code.
{% comment %}
https://gist.github.com/Sfshaza/7a9764702c0608711e08
https://dartpad.dartlang.org/7a9764702c0608711e08
{% prettify dart %}
import 'dart:math';
class Point {
final num x;
final num y;
final num distanceFromOrigin;
Point(num x, num y)
: x = x,
y = y,
distanceFromOrigin = sqrt(x * x + y * y);
}
void main() {
var p = Point(2, 3);
print(p.distanceFromOrigin);
}
{% endprettify %}
{% endcomment %}
<iframe
src="{{site.custom.dartpad.embed-inline-prefix}}?id=7a9764702c0608711e08&verticalRatio=85"
width="100%"
height="420px"
style="border: 1px solid #ccc;">
</iframe>
Sometimes a constructor’s only purpose is to redirect to another
constructor in the same class. A redirecting constructor’s body is
empty, with the constructor call appearing after a colon (:).
{% prettify dart %}
class Point {
num x, y;
// The main constructor for this class.
Point(this.x, this.y);
// Delegates to the main constructor.
Point.alongXAxis(num x) : this(x, 0);
}
{% endprettify %}
If your class produces objects that never change, you can make these
objects compile-time constants. To do this, define a const
constructor
and make sure that all instance variables are final
.
{% prettify dart %}
class ImmutablePoint {
static final ImmutablePoint origin =
const ImmutablePoint(0, 0);
final num x, y;
const ImmutablePoint(this.x, this.y);
}
{% endprettify %}
Constant constructors don't always create constants.
For details, see the section on
using constructors.
Use the factory
keyword when implementing a constructor that doesn’t
always create a new instance of its class. For example, a factory
constructor might return an instance from a cache, or it might return an
instance of a subtype.
The following example demonstrates a factory constructor returning
objects from a cache:
{% prettify dart %}
class Logger {
final String name;
bool mute = false;
// _cache is library-private, thanks to
// the _ in front of its name.
static final Map<String, Logger> _cache =
<String, Logger>{};
factory Logger(String name) {
if (_cache.containsKey(name)) {
return _cache[name];
} else {
final logger = Logger._internal(name);
_cache[name] = logger;
return logger;
}
}
Logger._internal(this.name);
void log(String msg) {
if (!mute) print(msg);
}
}
{% endprettify %}
**Note:**
Factory constructors have no access to `this`.
Invoke a factory constructor just like you would any other constructor:
{% prettify dart %}
var logger = Logger('UI');
logger.log('Button clicked');
{% endprettify %}
Methods are functions that provide behavior for an object.
Instance methods on objects can access instance variables and this
.
The distanceTo()
method in the following sample is an example of an
instance method:
{% prettify dart %}
import 'dart:math';
class Point {
num x, y;
Point(this.x, this.y);
num distanceTo(Point other) {
var dx = x - other.x;
var dy = y - other.y;
return sqrt(dx * dx + dy * dy);
}
}
{% endprettify %}
Getters and setters are special methods that provide read and write
access to an object’s properties. Recall that each instance variable has
an implicit getter, plus a setter if appropriate. You can create
additional properties by implementing getters and setters, using the
get
and set
keywords:
{% prettify dart %}
class Rectangle {
num left, top, width, height;
Rectangle(this.left, this.top, this.width, this.height);
// Define two calculated properties: right and bottom.
num get right => left + width;
set right(num value) => left = value - width;
num get bottom => top + height;
set bottom(num value) => top = value - height;
}
void main() {
var rect = Rectangle(3, 4, 20, 15);
assert(rect.left == 3);
rect.right = 12;
assert(rect.left == -8);
}
{% endprettify %}
With getters and setters, you can start with instance variables, later
wrapping them with methods, all without changing client code.
**Note:**
Operators such as increment (++) work in the expected way, whether or
not a getter is explicitly defined. To avoid any unexpected side
effects, the operator calls the getter exactly once, saving its value
in a temporary variable.
Instance, getter, and setter methods can be abstract, defining an
interface but leaving its implementation up to other classes.
Abstract methods can only exist in abstract classes.
To make a method abstract, use a semicolon (;) instead of a method body:
{% prettify dart %}
abstract class Doer {
// Define instance variables and methods...
void doSomething(); // Define an abstract method.
}
class EffectiveDoer extends Doer {
void doSomething() {
// Provide an implementation, so the method is not abstract here...
}
}
{% endprettify %}
Use the abstract
modifier to define an abstract class—a class that
can’t be instantiated. Abstract classes are useful for defining
interfaces, often with some implementation. If you want your abstract
class to appear to be instantiable, define a factory
constructor.
Abstract classes often have abstract methods.
Here’s an example of declaring an abstract class that has an abstract
method:
{% prettify dart %}
// This class is declared abstract and thus
// can't be instantiated.
abstract class AbstractContainer {
// Define constructors, fields, methods...
void updateChildren(); // Abstract method.
}
{% endprettify %}
Every class implicitly defines an interface containing all the instance
members of the class and of any interfaces it implements. If you want to
create a class A that supports class B’s API without inheriting B’s
implementation, class A should implement the B interface.
A class implements one or more interfaces by declaring them in an
implements
clause and then providing the APIs required by the
interfaces. For example:
{% prettify dart %}
// A person. The implicit interface contains greet().
class Person {
// In the interface, but visible only in this library.
final _name;
// Not in the interface, since this is a constructor.
Person(this._name);
// In the interface.
String greet(String who) => 'Hello, $who. I am $_name.';
}
// An implementation of the Person interface.
class Impostor implements Person {
get _name => '';
String greet(String who) => 'Hi $who. Do you know who I am?';
}
String greetBob(Person person) => person.greet('Bob');
void main() {
print(greetBob(Person('Kathy')));
print(greetBob(Impostor()));
}
{% endprettify %}
Here’s an example of specifying that a class implements multiple
interfaces:
{% prettify dart %}
class Point implements Comparable, Location {...}
{% endprettify %}
Use extends
to create a subclass, and super
to refer to the
superclass:
{% prettify dart %}
class Television {
void turnOn() {
_illuminateDisplay();
_activateIrSensor();
}
// ···
}
class SmartTelevision [!extends!] Television {
void turnOn() {
[!super!].turnOn();
_bootNetworkInterface();
_initializeMemory();
_upgradeApps();
}
// ···
}
{% endprettify %}
Subclasses can override instance methods, getters, and setters.
You can use the @override
annotation to indicate that you are
intentionally overriding a member:
{% prettify dart %}
class SmartTelevision extends Television {
[!@override!]
void turnOn() {...}
// ···
}
{% endprettify %}
To narrow the type of a method parameter or instance variable in code that is
type safe,
you can use the covariant
keyword.
You can override the operators shown in the following table.
For example, if you define a
Vector class, you might define a +
method to add two vectors.
<
| +
| |
| []
>
| /
| ^
| []=
<=
| ~/
| &
| ~
>=
| *
| <<
| ==
–
| %
| >>
{:.table}
Here’s an example of a class that overrides the +
and -
operators:
{% prettify dart %}
class Vector {
final int x, y;
Vector(this.x, this.y);
Vector operator +(Vector v) => Vector(x + v.x, y + v.y);
Vector operator -(Vector v) => Vector(x - v.x, y - v.y);
// Operator == and hashCode not shown. For details, see note below.
// ···
}
void main() {
final v = Vector(2, 3);
final w = Vector(2, 2);
assert(v + w == Vector(4, 5));
assert(v - w == Vector(0, 1));
}
{% endprettify %}
If you override ==
, you should also override Object's hashCode
getter.
For an example of overriding ==
and hashCode
, see
Implementing map keys.
For more information on overriding, in general, see
Extending a class.
To detect or react whenever code attempts to use a non-existent method or
instance variable, you can override noSuchMethod()
:
{% prettify dart %}
class A {
// Unless you override noSuchMethod, using a
// non-existent member results in a NoSuchMethodError.
@override
void [!noSuchMethod!](Invocation invocation) {
print('You tried to use a non-existent member: ' +
'${invocation.memberName}');
}
}
{% endprettify %}
You can't invoke an unimplemented method unless
one of the following is true:
-
The receiver has the static type dynamic
.
-
The receiver has a static type that
defines the unimplemented method (abstract is OK),
and the dynamic type of the receiver has an implemention of noSuchMethod()
that's different from the one in class Object
.
For more information, see the informal
noSuchMethod forwarding specification.
Enumerated types, often called enumerations or enums,
are a special kind of class used to represent
a fixed number of constant values.
Declare an enumerated type using the enum
keyword:
{% prettify dart %}
enum Color { red, green, blue }
{% endprettify %}
Each value in an enum has an index
getter,
which returns the zero-based position of the value in the enum declaration.
For example, the first value has index 0,
and the second value has index 1.
{% prettify dart %}
assert(Color.red.index == 0);
assert(Color.green.index == 1);
assert(Color.blue.index == 2);
{% endprettify %}
To get a list of all of the values in the enum,
use the enum's values
constant.
{% prettify dart %}
List colors = Color.values;
assert(colors[2] == Color.blue);
{% endprettify %}
You can use enums in switch statements, and
you'll get a warning if you don't handle all of the enum's values:
{% prettify dart %}
var aColor = Color.blue;
switch (aColor) {
case Color.red:
print('Red as roses!');
break;
case Color.green:
print('Green as grass!');
break;
default: // Without this, you see a WARNING.
print(aColor); // 'Color.blue'
}
{% endprettify %}
Enumerated types have the following limits:
- You can't subclass, mix in, or implement an enum.
- You can't explicitly instantiate an enum.
For more information, see the
Dart Language Specification.
Mixins are a way of reusing a class's code in multiple class
hierarchies.
To use a mixin, use the with
keyword followed by one or more mixin
names. The following example shows two classes that use mixins:
{% prettify dart %}
class Musician extends Performer [!with Musical!] {
// ···
}
class Maestro extends Person
[!with Musical, Aggressive, Demented!] {
Maestro(String maestroName) {
name = maestroName;
canConduct = true;
}
}
{% endprettify %}
To implement a mixin, create a class that extends Object,
declares no constructors, and has no calls to super
. For example:
{% prettify dart %}
abstract class Musical {
bool canPlayPiano = false;
bool canCompose = false;
bool canConduct = false;
void entertainMe() {
if (canPlayPiano) {
print('Playing piano');
} else if (canConduct) {
print('Waving hands');
} else {
print('Humming to self');
}
}
}
{% endprettify %}
**Note:** Some restrictions on mixins are being removed. For details, see the
[proposed mixin specification.][Dart 2.0 Mixins]
For a theoretical presentation of the evolution of mixins in Dart, see
A Brief History of Mixins in Dart.
Use the static
keyword to implement class-wide variables and methods.
Static variables (class variables) are useful for class-wide state and
constants:
{% prettify dart %}
class Queue {
static const initialCapacity = 16;
// ···
}
void main() {
assert(Queue.initialCapacity == 16);
}
{% endprettify %}
Static variables aren’t initialized until they’re used.
**Note:**
This page follows the [style guide
recommendation](/guides/language/effective-dart/style#identifiers)
of preferring `lowerCamelCase` for constant names.
Static methods (class methods) do not operate on an instance, and thus
do not have access to this
. For example:
{% prettify dart %}
import 'dart:math';
class Point {
num x, y;
Point(this.x, this.y);
static num distanceBetween(Point a, Point b) {
var dx = a.x - b.x;
var dy = a.y - b.y;
return sqrt(dx * dx + dy * dy);
}
}
void main() {
var a = Point(2, 2);
var b = Point(4, 4);
var distance = Point.distanceBetween(a, b);
assert(2.8 < distance && distance < 2.9);
print(distance);
}
{% endprettify %}
**Note:**
Consider using top-level functions, instead of static methods, for
common or widely used utilities and functionality.
You can use static methods as compile-time constants. For example, you
can pass a static method as a parameter to a constant constructor.
If you look at the API documentation for the basic array type,
List, you’ll see that the
type is actually List<E>
. The <...> notation marks List as a
generic (or parameterized) type—a type that has formal type
parameters. By convention, type variables have single-letter names, such
as E, T, S, K, and V.
Generics are often required for type safety, but they have more benefits
than just allowing your code to run:
- Properly specifying generic types results in better generated code.
- You can use generics to reduce code duplication.
If you intend for a list to contain only strings, you can
declare it as List<String>
(read that as “list of string”). That way
you, your fellow programmers, and your tools can detect that assigning a non-string to
the list is probably a mistake. Here’s an example:
{:.fails-sa}
{% prettify dart %}
var names = List();
names.addAll(['Seth', 'Kathy', 'Lars']);
names.add(42); // Error
{% endprettify %}
Another reason for using generics is to reduce code duplication.
Generics let you share a single interface and implementation between
many types, while still taking advantage of static
analysis. For example, say you create an interface for
caching an object:
{% prettify dart %}
abstract class ObjectCache {
Object getByKey(String key);
void setByKey(String key, Object value);
}
{% endprettify %}
You discover that you want a string-specific version of this interface,
so you create another interface:
{% prettify dart %}
abstract class StringCache {
String getByKey(String key);
void setByKey(String key, String value);
}
{% endprettify %}
Later, you decide you want a number-specific version of this
interface... You get the idea.
Generic types can save you the trouble of creating all these interfaces.
Instead, you can create a single interface that takes a type parameter:
{% prettify dart %}
abstract class Cache {
T getByKey(String key);
void setByKey(String key, T value);
}
{% endprettify %}
In this code, T is the stand-in type. It’s a placeholder that you can
think of as a type that a developer will define later.
List and map literals can be parameterized. Parameterized literals are
just like the literals you’ve already seen, except that you add
<type>
(for lists) or
<keyType, valueType>
(for maps)
before the opening bracket. Here
is example of using typed literals:
{% prettify dart %}
var names = ['Seth', 'Kathy', 'Lars'];
var pages = <String, String>{
'index.html': 'Homepage',
'robots.txt': 'Hints for web robots',
'humans.txt': 'We are people, not machines'
};
{% endprettify %}
To specify one or more types when using a constructor, put the types in
angle brackets (<...>
) just after the class name. For example:
{% prettify dart %}
var names = List();
names.addAll(['Seth', 'Kathy', 'Lars']);
var nameSet = Set.from(names);
{% endprettify %}
The following code creates a map that has integer keys and values of
type View:
{% prettify dart %}
var views = Map<int, View>();
{% endprettify %}
Dart generic types are reified, which means that they carry their type
information around at runtime. For example, you can test the type of a
collection:
{% prettify dart %}
var names = List();
names.addAll(['Seth', 'Kathy', 'Lars']);
print(names is List); // true
{% endprettify %}
**Note:**
In contrast, generics in Java use *erasure*, which means that generic
type parameters are removed at runtime. In Java, you can test whether
an object is a List, but you can’t test whether it’s a `List`.
When implementing a generic type,
you might want to limit the types of its parameters.
You can do this using extends
.
{% prettify dart %}
class Foo<T [!extends SomeBaseClass!]> {
// Implementation goes here...
String toString() => "Instance of 'Foo<$T>'";
}
class Extender extends SomeBaseClass {...}
{% endprettify %}
It's OK to use SomeBaseClass
or any of its subclasses as generic argument:
{% prettify dart %}
var someBaseClassFoo = !Foo!;
var extenderFoo = !Foo!;
{% endprettify %}
It's also OK to specify no generic argument:
{% prettify dart %}
var foo = Foo();
print(foo); // Instance of 'Foo'
{% endprettify %}
Specifying any non-SomeBaseClass
type results in an error:
{:.fails-sa}
{% prettify dart %}
var foo = !Foo!;
{% endprettify %}
Initially, Dart's generic support was limited to classes.
A newer syntax, called generic methods, allows type arguments on methods and functions:
{% prettify dart %}
[!T!] first[!!](List<[!T!]> ts) {
// Do some initial work or error checking, then...
[!T!] tmp = ts[0];
// Do some additional checking or processing...
return tmp;
}
{% endprettify %}
Here the generic type parameter on first
(<T>
)
allows you to use the type argument T
in several places:
- In the function's return type (
T
).
- In the type of an argument (
List<T>
).
- In the type of a local variable (
T tmp
).
For more information about generics, see
Using Generic Methods.
The import
and library
directives can help you create a
modular and shareable code base. Libraries not only provide APIs, but
are a unit of privacy: identifiers that start with an underscore (_)
are visible only inside the library. Every Dart app is a library, even
if it doesn’t use a library
directive.
Libraries can be distributed using packages. See
Pub Package and Asset Manager
for information about
pub, a package manager included in the SDK.
Use import
to specify how a namespace from one library is used in the
scope of another library.
For example, Dart web apps generally use the dart:html
library, which they can import like this:
{% prettify dart %}
import 'dart:html';
{% endprettify %}
The only required argument to import
is a URI specifying the
library.
For built-in libraries, the URI has the special dart:
scheme.
For other libraries, you can use a file system path or the package:
scheme. The package:
scheme specifies libraries provided by a package
manager such as the pub tool. For example:
{% prettify dart %}
import 'package:test/test.dart';
{% endprettify %}
**Note:**
*URI* stands for uniform resource identifier.
*URLs* (uniform resource locators) are a common kind of URI.
If you import two libraries that have conflicting identifiers, then you
can specify a prefix for one or both libraries. For example, if library1
and library2 both have an Element class, then you might have code like
this:
{% prettify dart %}
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
// Uses Element from lib1.
Element element1 = Element();
// Uses Element from lib2.
lib2.Element element2 = lib2.Element();
{% endprettify %}
If you want to use only part of a library, you can selectively import
the library. For example:
{% prettify dart %}
// Import only foo.
import 'package:lib1/lib1.dart' show foo;
// Import all names EXCEPT foo.
import 'package:lib2/lib2.dart' hide foo;
{% endprettify %}
Deferred loading (also called lazy loading)
allows an application to load a library on demand,
if and when it's needed.
Here are some cases when you might use deferred loading:
- To reduce an app's initial startup time.
- To perform A/B testing—trying out
alternative implementations of an algorithm, for example.
- To load rarely used functionality, such as optional screens and dialogs.
To lazily load a library, you must first
import it using deferred as
.
{% prettify dart %}
import 'package:greetings/hello.dart' deferred as hello;
{% endprettify %}
When you need the library, invoke
loadLibrary()
using the library's identifier.
{% prettify dart %}
Future greet() async {
await hello.loadLibrary();
hello.printGreeting();
}
{% endprettify %}
In the preceding code,
the await
keyword pauses execution until the library is loaded.
For more information about async
and await
,
see asynchrony support.
You can invoke loadLibrary()
multiple times on a library without problems.
The library is loaded only once.
Keep in mind the following when you use deferred loading:
- A deferred library's constants aren't constants in the importing file.
Remember, these constants don't exist until the deferred library is loaded.
- You can't use types from a deferred library in the importing file.
Instead, consider moving interface types to a library imported by
both the deferred library and the importing file.
- Dart implicitly inserts
loadLibrary()
into the namespace that you define
using deferred as namespace
.
The loadLibrary()
function returns a Future.
**Dart VM difference:**
The Dart VM allows access to members of deferred libraries
even before the call to `loadLibrary()`.
This behavior might change, so
**don't depend on the current VM behavior.**
For details, see [issue #33118.](dart-lang/sdk#33118)
See
Create Library Packages
for advice on how to implement a library package, including:
- How to organize library source code.
- How to use the
export
directive.
- When to use the
part
directive.
Dart libraries are full of functions that
return Future or Stream objects.
These functions are asynchronous:
they return after setting up
a possibly time-consuming operation
(such as I/O),
without waiting for that operation to complete.
The async
and await
keywords support asynchronous programming,
letting you write asynchronous code that
looks similar to synchronous code.
When you need the result of a completed Future,
you have two options:
- Use
async
and await
.
- Use the Future API, as described
in the library tour.
Code that uses async
and await
is asynchronous,
but it looks a lot like synchronous code.
For example, here's some code that uses await
to wait for the result of an asynchronous function:
{% prettify dart %}
await lookUpVersion();
{% endprettify %}
To use await
, code must be in an async function—a
function marked as async
:
{% prettify dart %}
Future checkVersion() [!async!] {
var version = [!await!] lookUpVersion();
// Do something with version
}
{% endprettify %}
**Note:**
Although an async function might perform time-consuming operations,
it doesn't wait for those operations.
Instead, the async function executes only until it encounters
its first `await` expression
([details][synchronous-async-start]).
Then it returns a Future object,
resuming execution only after the `await` expression completes.
Use try
, catch
, and finally
to handle errors and cleanup in code that uses await
:
{% prettify dart %}
try {
version = await lookUpVersion();
} catch (e) {
// React to inability to look up the version
}
{% endprettify %}
You can use await
multiple times in an async function.
For example, the following code waits three times
for the results of functions:
{% prettify dart %}
var entrypoint = await findEntrypoint();
var exitCode = await runExecutable(entrypoint, args);
await flushThenExit(exitCode);
{% endprettify %}
In await expression
,
the value of expression
is usually a Future;
if it isn't, then the value is automatically wrapped in a Future.
This Future object indicates a promise to return an object.
The value of await expression
is that returned object.
The await expression makes execution pause until that object is available.
If you get a compile-time error when using await
,
make sure await
is in an async function.
For example, to use await
in your app's main()
function,
the body of main()
must be marked as async
:
{% prettify dart %}
Future main() [!async!] {
checkVersion();
print('In main: version is ${[!await!] lookUpVersion()}');
}
{% endprettify %}
An async function is a function whose body is marked with
the async
modifier.
Adding the async
keyword to a function makes it return a Future.
For example, consider this synchronous function,
which returns a String:
{% prettify dart %}
String lookUpVersion() => '1.0.0';
{% endprettify %}
If you change it to be an async function—for example,
because a future implementation will be time consuming—the
returned value is a Future:
{% prettify dart %}
Future lookUpVersion() async => '1.0.0';
{% endprettify %}
Note that the function's body doesn't need to use the Future API.
Dart creates the Future object if necessary.
If your function doesn't return a useful value,
make its return type Future<void>
.
{% comment %}
PENDING: add example here
Where else should we cover generalized void?
{% endcomment %}
When you need to get values from a Stream,
you have two options:
- Use
async
and an asynchronous for loop (await for
).
- Use the Stream API, as described
in the library tour.
**Note:**
Before using `await for`, be sure that it makes the code clearer
and that you really do want to wait for all of the stream's results.
For example, you usually should **not** use `await for` for UI event listeners,
because UI frameworks send endless streams of events.
An asynchronous for loop has the following form:
{% prettify dart %}
await for (varOrType identifier in expression) {
// Executes each time the stream emits a value.
}
{% endprettify %}
The value of expression
must have type Stream.
Execution proceeds as follows:
- Wait until the stream emits a value.
- Execute the body of the for loop,
with the variable set to that emitted value.
- Repeat 1 and 2 until the stream is closed.
To stop listening to the stream,
you can use a break
or return
statement,
which breaks out of the for loop
and unsubscribes from the stream.
If you get a compile-time error when implementing an asynchronous for loop,
make sure the await for
is in an async function.
For example, to use an asynchronous for loop in your app's main()
function,
the body of main()
must be marked as async
:
{% prettify dart %}
Future main() [!async!] {
// ...
[!await for!] (var request in requestServer) {
handleRequest(request);
}
// ...
}
{% endprettify %}
For more information about asynchronous programming, in general, see the
dart:async
section of the library tour.
Also see the articles
Dart Language Asynchrony Support: Phase 1
and
Dart Language Asynchrony Support: Phase 2,
and the Dart language specification.
When you need to lazily produce a sequence of values,
consider using a generator function.
Dart has built-in support for two kinds of generator functions:
To implement a synchronous generator function,
mark the function body as sync*
,
and use yield
statements to deliver values:
{% prettify dart %}
Iterable naturalsTo(int n) sync* {
int k = 0;
while (k < n) yield k++;
}
{% endprettify %}
To implement an asynchronous generator function,
mark the function body as async*
,
and use yield
statements to deliver values:
{% prettify dart %}
Stream asynchronousNaturalsTo(int n) async* {
int k = 0;
while (k < n) yield k++;
}
{% endprettify %}
If your generator is recursive,
you can improve its performance by using yield*
:
{% prettify dart %}
Iterable naturalsDownFrom(int n) sync* {
if (n > 0) {
yield n;
yield* naturalsDownFrom(n - 1);
}
}
{% endprettify %}
For more information about generators, see the article
Dart Language Asynchrony Support: Phase 2.
To allow your Dart class to be called like a function,
implement the call()
method.
In the following example, the WannabeFunction
class defines
a call() function that takes three strings and concatenates them,
separating each with a space, and appending an exclamation.
Click the run button {% asset red-run.png alt="" %} to execute the code.
{% comment %}
https://gist.github.com/405379bacf30335f3aed
https://dartpad.dartlang.org/405379bacf30335f3aed
{% prettify dart %}
class WannabeFunction {
String call(String a, String b, String c) => '$a $b $c!';
}
var wf = WannabeFunction();
var out = wf('Hi', 'there,', 'gang');
main() => print(out);
{% endprettify %}
{% endcomment %}
<iframe
src="{{site.custom.dartpad.embed-inline-prefix}}?id=405379bacf30335f3aed&verticalRatio=73"
width="100%"
height="240px"
style="border: 1px solid #ccc;">
</iframe>
For more information on treating classes like functions, see
Emulating Functions in Dart.
Most computers, even on mobile platforms, have multi-core CPUs.
To take advantage of all those cores, developers traditionally use
shared-memory threads running concurrently. However, shared-state
concurrency is error prone and can lead to complicated code.
Instead of threads, all Dart code runs inside of isolates. Each
isolate has its own memory heap, ensuring that no isolate’s state is
accessible from any other isolate.
For more information, see the
dart:isolate library documentation.
In Dart, functions are objects, just like strings and numbers are
objects. A typedef, or function-type alias, gives a function type a
name that you can use when declaring fields and return types. A typedef
retains type information when a function type is assigned to a variable.
Consider the following code, which doesn't use a typedef:
{% prettify dart %}
class SortedCollection {
Function compare;
SortedCollection(int f(Object a, Object b)) {
compare = f;
}
}
// Initial, broken implementation.
int sort(Object a, Object b) => 0;
void main() {
SortedCollection coll = SortedCollection(sort);
// All we know is that compare is a function,
// but what type of function?
assert(coll.compare is Function);
}
{% endprettify %}
Type information is lost when assigning f
to compare
. The type of
f
is (Object, ``Object)
→ int
(where → means returns), yet the
type of compare
is Function. If we change the code to use explicit
names and retain type information, both developers and tools can use
that information.
{% prettify dart %}
typedef Compare = int Function(Object a, Object b);
class SortedCollection {
Compare compare;
SortedCollection(this.compare);
}
// Initial, broken implementation.
int sort(Object a, Object b) => 0;
void main() {
SortedCollection coll = SortedCollection(sort);
assert(coll.compare is Function);
assert(coll.compare is Compare);
}
{% endprettify %}
**Note:**
Currently, typedefs are restricted to function types. We expect this
to change.
Because typedefs are simply aliases, they offer a way to check the type
of any function. For example:
{% prettify dart %}
typedef Compare = int Function(T a, T b);
int sort(int a, int b) => a - b;
void main() {
assert(sort is Compare); // True!
}
{% endprettify %}
Use metadata to give additional information about your code. A metadata
annotation begins with the character @
, followed by either a reference
to a compile-time constant (such as deprecated
) or a call to a
constant constructor.
Two annotations are available to all Dart code: @deprecated
and
@override
. For examples of using @override
,
see Extending a class.
Here’s an example of using the @deprecated
annotation:
{% prettify dart %}
class Television {
/// Deprecated: Use [turnOn] instead.
[!@deprecated!]
void activate() {
turnOn();
}
/// Turns the TV's power on.
void turnOn() {...}
}
{% endprettify %}
You can define your own metadata annotations. Here’s an example of
defining a @todo annotation that takes two arguments:
{% prettify dart %}
library todo;
class Todo {
final String who;
final String what;
const Todo(this.who, this.what);
}
{% endprettify %}
And here’s an example of using that @todo annotation:
{% prettify dart %}
import 'todo.dart';
@Todo('seth', 'make this do something')
void doSomething() {
print('do something');
}
{% endprettify %}
Metadata can appear before a library, class, typedef, type parameter,
constructor, factory, function, field, parameter, or variable
declaration and before an import or export directive. You can
retrieve metadata at runtime using reflection.
Dart supports single-line comments, multi-line comments, and
documentation comments.
A single-line comment begins with //
. Everything between //
and the
end of line is ignored by the Dart compiler.
{% prettify dart %}
void main() {
// TODO: refactor into an AbstractLlamaGreetingFactory?
print('Welcome to my Llama farm!');
}
{% endprettify %}
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.
{% prettify dart %}
void main() {
/*
- This is a lot of work. Consider raising chickens.
Llama larry = Llama();
larry.feed();
larry.exercise();
larry.clean();
*/
}
{% endprettify %}
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:
{% prettify dart %}
/// 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) {
// ...
}
}
{% endprettify %}
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.
For an example of generated documentation, see the Dart API
documentation. For advice on how to structure
your comments, see
Guidelines for Dart Doc Comments.
This page summarized the commonly used features in the Dart language.
More features are being implemented, but we expect that they won’t break
existing code. For more information, see the Dart Language
Specification and
Effective Dart.
To learn more about Dart's core libraries, see
A Tour of the Dart Libraries.