Dart Programming Language
Dart is an open-source general-purpose programming language. It is originally
developed by Google and later approved as a standard by ECMA. Dart is a new
programming language meant for the server as well as the browser. Introduced
by Google, the Dart SDK ships with its compiler – the Dart VM. The SDK also
includes a utility -dart2js, a transpiler that generates JavaScript equivalent of a
Dart Script. This tutorial provides a basic level understanding of the Dart
programming language.
Dart is an object-oriented language with C-style syntax which can optionally trans
compile into JavaScript. It supports a varied range of programming aids like
interfaces, classes, collections, generics, and optional typing.
Dart can be extensively used to create single-page applications. Single-page
applications apply only to websites and web applications. Single-page
applications enable navigation between different screens of the website without
loading a different webpage in the browser. A classic example is GMail ─ when
you click on a message in your inbox, browser stays on the same webpage, but
JavaScript code hides the inbox and brings the message body on screen.
Google has released a special build of Chromium – the Dart VM. Using Dartium
means you don’t have to compile your code to JavaScript until you’re ready to test
on other browsers.
The following table compares the features of Dart and JavaScript.
Introduction to Dart
This page provides a brief introduction to the Dart language through samples of
its main features.
To learn more about the Dart language, visit the in-depth, individual topic pages
listed under Language in the left side menu.
For coverage of Dart's core libraries, check out the core
library
documentation. You can also check out the Dart cheatsheet, for a
more interactive introduction.
Hello World
Every app requires the top-level main() function, where execution starts.
Functions that don't explicitly return a value have the void return type. To
display text on the console, you can use the top-level print() function:
dart
void main() {
print('Hello, World!');
content_copy
Read more about the main() function in Dart, including optional
parameters for command-line arguments.
Variables
Even in type-safe Dart code, you can declare most variables without explicitly
specifying their type using var. Thanks to type inference, these variables' types
are determined by their initial values:
dart
var name = 'Voyager I';
var year = 1977;
var antennaDiameter = 3.7;
var flybyObjects = ['Jupiter', 'Saturn', 'Uranus',
'Neptune'];
var image = {
'tags': ['saturn'],
'url': '//path/to/saturn.jpg'
};
content_copy
Read more about variables in Dart, including default values,
the final and const keywords, and static types.
Control flow statements
Dart supports the usual control flow statements:
dart
if (year >= 2001) {
print('21st century');
} else if (year >= 1901) {
print('20th century');
for (final object in flybyObjects) {
print(object);
for (int month = 1; month <= 12; month++) {
print(month);
while (year < 2016) {
year += 1;
content_copy
Read more about control flow statements in Dart,
including break and continue, switch and case, and assert.
Functions
We recommend specifying the types of each function's arguments and
return value:
dart
int fibonacci(int n) {
if (n == 0 || n == 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
var result = fibonacci(20);
content_copy
A shorthand => (arrow) syntax is handy for functions that contain a single
statement. This syntax is especially useful when passing anonymous functions as
arguments:
dart
flybyObjects.where((name) =>
name.contains('turn')).forEach(print);
content_copy
Besides showing an anonymous function (the argument to where()), this code
shows that you can use a function as an argument: the top-
level print() function is an argument to forEach().
Read more about functions in Dart, including optional parameters, default
parameter values, and lexical scope.
Comments
Dart comments usually start with //.
dart
// This is a normal, one-line comment.
/// This is a documentation comment, used to document
libraries,
/// classes, and their members. Tools like IDEs and
dartdoc treat
/// doc comments specially.
/* Comments like these are also supported. */
content_copy
Read more about comments in Dart, including how the documentation
tooling works.
Imports
To access APIs defined in other libraries, use import.
dart
// Importing core libraries
import 'dart:math';
// Importing libraries from external packages
import 'package:test/test.dart';
// Importing files
import 'path/to/my_other_file.dart';
content_copy
Read more about libraries and visibility in Dart, including library
prefixes, show and hide, and lazy loading through the deferred keyword.
Classes
Here's an example of a class with three properties, two constructors, and a
method. One of the properties can't be set directly, so it's defined using a getter
method (instead of a variable). The method uses string interpolation to print
variables' string equivalents inside of string literals.
dart
class Spacecraft {
String name;
DateTime? launchDate;
// Read-only non-final property
int? get launchYear => launchDate?.year;
// Constructor, with syntactic sugar for assignment to
members.
Spacecraft(this.name, this.launchDate) {
// Initialization code goes here.
// Named constructor that forwards to the default one.
Spacecraft.unlaunched(String name) : this(name,
null);
// Method.
void describe() {
print('Spacecraft: $name');
// Type promotion doesn't work on getters.
var launchDate = this.launchDate;
if (launchDate != null) {
int years =
DateTime.now().difference(launchDate).inDays ~/ 365;
print('Launched: $launchYear ($years years ago)');
} else {
print('Unlaunched');
content_copy
Read more about strings, including string interpolation, literals, expressions,
and the toString() method.
You might use the Spacecraft class like this:
dart
var voyager = Spacecraft('Voyager I', DateTime(1977, 9,
5));
voyager.describe();
var voyager3 = Spacecraft.unlaunched('Voyager III');
voyager3.describe();
content_copy
Read more about classes in Dart, including initializer lists,
optional new and const, redirecting constructors, factory constructors,
getters, setters, and much more.
Enums
Enums are a way of enumerating a predefined set of values or instances in a way
which ensures that there cannot be any other instances of that type.
Here is an example of a simple enum that defines a simple list of predefined
planet types:
dart
enum PlanetType { terrestrial, gas, ice }
content_copy
Here is an example of an enhanced enum declaration of a class describing
planets, with a defined set of constant instances, namely the planets of our own
solar system.
dart
/// Enum that enumerates the different planets in our
solar system
/// and some of their properties.
enum Planet {
mercury(planetType: PlanetType.terrestrial, moons: 0,
hasRings: false),
venus(planetType: PlanetType.terrestrial, moons: 0,
hasRings: false),
// ···
uranus(planetType: PlanetType.ice, moons: 27,
hasRings: true),
neptune(planetType: PlanetType.ice, moons: 14,
hasRings: true);
/// A constant generating constructor
const Planet(
{required this.planetType, required this.moons,
required this.hasRings});
/// All instance variables are final
final PlanetType planetType;
final int moons;
final bool hasRings;
/// Enhanced enums support getters and other methods
bool get isGiant =>
planetType == PlanetType.gas || planetType ==
PlanetType.ice;
}
content_copy
You might use the Planet enum like this:
dart
final yourPlanet = Planet.earth;
if (!yourPlanet.isGiant) {
print('Your planet is not a "giant planet".');
content_copy
Read more about enums in Dart, including enhanced enum requirements,
automatically introduced properties, accessing enumerated value names, switch
statement support, and much more.
Inheritance
Dart has single inheritance.
dart
class Orbiter extends Spacecraft {
double altitude;
Orbiter(super.name, DateTime super.launchDate,
this.altitude);
content_copy
Read more about extending classes, the optional @override annotation,
and more.
Mixins
Mixins are a way of reusing code in multiple class hierarchies. The following is a
mixin declaration:
dart
mixin Piloted {
int astronauts = 1;
void describeCrew() {
print('Number of astronauts: $astronauts');
content_copy
To add a mixin's capabilities to a class, just extend the class with the mixin.
dart
class PilotedCraft extends Spacecraft with Piloted {
// ···
content_copy
PilotedCraft now has the astronauts field as well as
the describeCrew() method.
Read more about mixins.
Interfaces and abstract classes
All classes implicitly define an interface. Therefore, you can implement any class.
dart
class MockSpaceship implements Spacecraft {
// ···
content_copy
Read more about implicitinterfaces, or about the
explicit interface keyword.
You can create an abstract class to be extended (or implemented) by a concrete
class. Abstract classes can contain abstract methods (with empty bodies).
dart
abstract class Describable {
void describe();
void describeWithEmphasis() {
print('=========');
describe();
print('=========');
content_copy
Any class extending Describable has
the describeWithEmphasis() method, which calls the extender's
implementation of describe().
Read more about abstract classes and methods.
Async
Avoid callback hell and make your code much more readable by
using async and await.
dart
const oneSecond = Duration(seconds: 1);
// ···
Future<void> printWithDelay(String message) async {
await Future.delayed(oneSecond);
print(message);
content_copy
The method above is equivalent to:
dart
Future<void> printWithDelay(String message) {
return Future.delayed(oneSecond).then((_) {
print(message);
});
content_copy
As the next example shows, async and await help make asynchronous code
easy to read.
dart
Future<void> createDescriptions(Iterable<String>
objects) async {
for (final object in objects) {
try {
var file = File('$object.txt');
if (await file.exists()) {
var modified = await file.lastModified();
print(
'File for $object already exists. It was modified
on $modified.');
continue;
await file.create();
await file.writeAsString('Start describing $object in
this file.');
} on IOException catch (e) {
print('Cannot create description for $object: $e');
}
}
content_copy
You can also use async*, which gives you a nice, readable way to build
streams.
dart
Stream<String> report(Spacecraft craft,
Iterable<String> objects) async* {
for (final object in objects) {
await Future.delayed(oneSecond);
yield '${craft.name} flies by $object';
content_copy
Read more about asynchrony support,
including async functions, Future, Stream, and the asynchronous loop
(await for).
Exceptions
To raise an exception, use throw:
dart
if (astronauts == 0) {
throw StateError('No astronauts.');
content_copy
To catch an exception, use a try statement with on or catch (or both):
dart
Future<void> describeFlybyObjects(List<String>
flybyObjects) async {
try {
for (final object in flybyObjects) {
var description = await
File('$object.txt').readAsString();
print(description);
} on IOException catch (e) {
print('Could not describe object: $e');
} finally {
flybyObjects.clear();
}
}
content_copy
Note that the code above is asynchronous; try works for both synchronous code
and code in an async function.
Read more about exceptions, including stack traces, rethrow, and the
difference between Error and Exception.
Important concepts
As you continue to 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. With the
exception of null (if you enable sound null safety), all objects inherit
from the Object class.
merge_typeVersion note
Null safety was introduced in Dart 2.12. Using null safety requires
a language version of at least 2.12.
Although Dart is strongly typed, type annotations are optional because Dart can
infer types. In var number = 101, number is inferred to be of type int.
If you enable null safety, variables can't contain null unless you say they
can. You can make a variable nullable by putting a question mark (?) at the end
of its type. For example, a variable of type int? might be an integer, or it might
be null. If you know that an expression never evaluates to null but Dart
disagrees, you can add ! to assert that it isn't null (and to throw an exception if it
is). An example: int x = nullableButNotNullInt!
When you want to explicitly say that any type is allowed, use the
type Object? (if you've enabled null safety), Object, or—if you must defer
type checking until runtime—the special type dynamic.
Dart supports generic types, like List<int> (a list of integers)
or List<Object> (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,
and private. If an identifier starts with an underscore (_), it's private to its
library. For details, see Libraries and imports.
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
of expr1 or expr2. 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.
dart:core
The dart:core library (API reference) provides a small but
critical set of built-in functionality. This library is
automatically imported into every Dart program.
Printing to the console
The top-level print() method takes a single argument (any
Object) and displays that object's string value (as returned
by toString()) in the console.
dart
print(anObject);
print('I drink $tea.');
content_copy
For more information on basic strings and toString(),
see Strings in the language tour.
Numbers
The dart:core library defines the num, int, and double
classes, which have some basic utilities for working with
numbers.
You can convert a string into an integer or double with
the parse() methods of int and double, respectively:
dart
assert(int.parse('42') == 42);
assert(int.parse('0x42') == 66);
assert(double.parse('0.50') == 0.5);
content_copy
Or use the parse() method of num, which creates an integer
if possible and otherwise a double:
dart
assert(num.parse('42') is int);
assert(num.parse('0x42') is int);
assert(num.parse('0.50') is double);
content_copy
To specify the base of an integer, add a radix parameter:
dart
assert(int.parse('42', radix: 16) == 66);
content_copy
Use the toString() method to convert an int or double to a
string. To specify the number of digits to the right of the
decimal, use toStringAsFixed(). To specify the number of
significant digits in the string, use toStringAsPrecision():
dart
// Convert an int to a string.
assert(42.toString() == '42');
// Convert a double to a string.
assert(123.456.toString() == '123.456');
// Specify the number of digits after the decimal.
assert(123.456.toStringAsFixed(2) == '123.46');
// Specify the number of significant figures.
assert(123.456.toStringAsPrecision(2) == '1.2e+2');
assert(double.parse('1.2e+2') == 120.0);
content_copy
For more information, see the API documentation
for int, double, and num. Also see the dart:math section
Strings and regular expressions
A string in Dart is an immutable sequence of UTF-16 code
units. The language tour has more information
about strings. You can use regular expressions (RegExp
objects) to search within strings and to replace parts of
strings.
The String class defines such methods
as split(), contains(), startsWith(), endsWith(), and more.
Searching inside a string
You can find particular locations within a string, as well as
check whether a string begins with or ends with a particular
pattern. For example:
dart
// Check whether a string contains another string.
assert('Never odd or even'.contains('odd'));
// Does a string start with another string?
assert('Never odd or even'.startsWith('Never'));
// Does a string end with another string?
assert('Never odd or even'.endsWith('even'));
// Find the location of a string inside a string.
assert('Never odd or even'.indexOf('odd') == 6);
content_copy
Extracting data from a string
You can get the individual characters from a string as
Strings or ints, respectively. To be precise, you actually get
individual UTF-16 code units; high-numbered characters
such as the treble clef symbol ('\u{1D11E}') are two code
units apiece.
You can also extract a substring or split a string into a list of
substrings:
dart
// Grab a substring.
assert('Never odd or even'.substring(6, 9) == 'odd');
// Split a string using a string pattern.
var parts = 'progressive web apps'.split(' ');
assert(parts.length == 3);
assert(parts[0] == 'progressive');
// Get a UTF-16 code unit (as a string) by index.
assert('Never odd or even'[0] == 'N');
// Use split() with an empty string parameter to get
// a list of all characters (as Strings); good for
// iterating.
for (final char in 'hello'.split('')) {
print(char);
// Get all the UTF-16 code units in the string.
var codeUnitList = 'Never odd or even'.codeUnits.toList();
assert(codeUnitList[0] == 78);
content_copy
::: In many cases, you want to work with Unicode grapheme
clusters as opposed to pure code units. These are characters
as they are perceived by the user (for example, "🇬🇧" is one
user-perceived character but several UTF-16 code units). For
this, the Dart team provides the characters package. :::
Converting to uppercase or lowercase
You can easily convert strings to their uppercase and
lowercase variants:
dart
// Convert to uppercase.
assert('web apps'.toUpperCase() == 'WEB APPS');
// Convert to lowercase.
assert('WEB APPS'.toLowerCase() == 'web apps');
content_copy
infoNote
These methods don't work for every language. For example, the Turkish
alphabet's dotless I is converted incorrectly.
Trimming and empty strings
Remove all leading and trailing white space with trim(). To
check whether a string is empty (length is zero),
use isEmpty.
dart
// Trim a string.
assert(' hello '.trim() == 'hello');
// Check whether a string is empty.
assert(''.isEmpty);
// Strings with only white space are not empty.
assert(' '.isNotEmpty);
content_copy
Replacing part of a string
Strings are immutable objects, which means you can create
them but you can't change them. If you look closely at
the String API reference, you'll notice that none of the
methods actually changes the state of a String. For
example, the method replaceAll() returns a new String
without changing the original String:
dart
var greetingTemplate = 'Hello, NAME!';
var greeting = greetingTemplate.replaceAll(RegExp('NAME'), 'Bob');
// greetingTemplate didn't change.
assert(greeting != greetingTemplate);
content_copy
Building a string
To programmatically generate a string, you can use
StringBuffer. A StringBuffer doesn't generate a new String
object until toString() is called. The writeAll() method has
an optional second parameter that lets you specify a
separator—in this case, a space.
dart
var sb = StringBuffer();
sb
..write('Use a StringBuffer for ')
..writeAll(['efficient', 'string', 'creation'], ' ')
..write('.');
var fullString = sb.toString();
assert(fullString == 'Use a StringBuffer for efficient string creation.');
content_copy
Regular expressions
The RegExp class provides the same capabilities as
JavaScript regular expressions. Use regular expressions for
efficient searching and pattern matching of strings.
dart
// Here's a regular expression for one or more digits.
var numbers = RegExp(r'\d+');
var allCharacters = 'llamas live fifteen to twenty years';
var someDigits = 'llamas live 15 to 20 years';
// contains() can use a regular expression.
assert(!allCharacters.contains(numbers));
assert(someDigits.contains(numbers));
// Replace every match with another string.
var exedOut = someDigits.replaceAll(numbers, 'XX');
assert(exedOut == 'llamas live XX to XX years');
content_copy
You can work directly with the RegExp class, too. The Match
class provides access to a regular expression match.
dart
var numbers = RegExp(r'\d+');
var someDigits = 'llamas live 15 to 20 years';
// Check whether the reg exp has a match in a string.
assert(numbers.hasMatch(someDigits));
// Loop through all matches.
for (final match in numbers.allMatches(someDigits)) {
print(match.group(0)); // 15, then 20
content_copy
More information
Refer to the String API reference for a full list of methods.
Also see the API reference
for StringBuffer, Pattern, RegExp, and Match.
Collections
Dart ships with a core collections API, which includes classes
for lists, sets, and maps.
lightbulbTip
To practice using APIs that are available to both lists and sets, follow the Iterable
collections tutorial.
Lists
As the language tour shows, you can use literals to create
and initialize lists. Alternatively, use one of the List
constructors. The List class also defines several methods for
adding items to and removing items from lists.
dart
// Create an empty list of strings.
var grains = <String>[];
assert(grains.isEmpty);
// Create a list using a list literal.
var fruits = ['apples', 'oranges'];
// Add to a list.
fruits.add('kiwis');
// Add multiple items to a list.
fruits.addAll(['grapes', 'bananas']);
// Get the list length.
assert(fruits.length == 5);
// Remove a single item.
var appleIndex = fruits.indexOf('apples');
fruits.removeAt(appleIndex);
assert(fruits.length == 4);
// Remove all elements from a list.
fruits.clear();
assert(fruits.isEmpty);
// You can also create a List using one of the constructors.
var vegetables = List.filled(99, 'broccoli');
assert(vegetables.every((v) => v == 'broccoli'));
content_copy
Use indexOf() to find the index of an object in a list:
dart
var fruits = ['apples', 'oranges'];
// Access a list item by index.
assert(fruits[0] == 'apples');
// Find an item in a list.
assert(fruits.indexOf('apples') == 0);
content_copy
Sort a list using the sort() method. You can provide a sorting
function that compares two objects. This sorting function
must return < 0 for smaller, 0 for the same, and > 0
for bigger. The following example uses compareTo(), which
is defined by Comparable and implemented by String.
dart
var fruits = ['bananas', 'apples', 'oranges'];
// Sort a list.
fruits.sort((a, b) => a.compareTo(b));
assert(fruits[0] == 'apples');
content_copy
Lists are parameterized types (generics), so you can specify
the type that a list should contain:
dart
// This list should contain only strings.
var fruits = <String>[];
fruits.add('apples');
var fruit = fruits[0];
assert(fruit is String);
content_copy
✗ static analysis: failuredart
fruits.add(5); // Error: 'int' can't be assigned to 'String'