- Dart cheatsheet codelab
- String interpolation
- Null-aware operators
- Conditional property access
- Collection literals
- Arrow syntax
- Cascades
- Getters and setters
- Optional positional parameters
- Optional named parameters
- Exceptions
- Using this in a constructor
- Initializer lists
- Named constructors
- Factory constructors
- Redirecting constructors
- Const constructors
- What next?
Dart cheatsheet codelab
The Dart language is designed to be easy to learn for coders coming from other languages, but it has a few unique features. This codelab — which is based on a Dart language cheatsheet written by and for Google engineers — walks you through the most important of these language features.
The embedded editors in this codelab have partially completed code snippets. You can use these editors to test your knowledge by completing the code and clicking the Run button. If you need help, click the Hint button. To run the code formatter (dartfmt), click Format. The Reset button erases your work and restores the editor to its original state.
Note: This page uses embedded DartPads to display runnable examples. If you see empty boxes instead of DartPads, go to the DartPad troubleshooting page.
String interpolation
To put the value of an expression inside a string, use ${expression}
. If the expression is an identifier, you can omit the {}
.
Here are some examples of using string interpolation:
String | Result | |
---|---|---|
‘${3 + 2}’ | ‘5’ | |
‘${“word”.toUpperCase()}’ | ‘WORD’ | |
‘$myObject’ | The value of myObject.toString() |
Code example
The following function takes two integers as parameters. Make it return a string containing both integers separated by a space. For example, stringify(2, 3)
should return '2 3'
.
Null-aware operators
Dart offers some handy operators for dealing with values that might be null. One is the ??=
assignment operator, which assigns a value to a variable only if that variable is currently null:
int a; // The initial value of a is null.
a ??= 3;
print(a); // <-- Prints 3.
a ??= 5;
print(a); // <-- Still prints 3.
Another null-aware operator is ??
, which returns the expression on its left unless that expression’s value is null, in which case it evaluates and returns the expression on its right:
print(1 ?? 3); // <-- Prints 1.
print(null ?? 12); // <-- Prints 12.
Code example
Try putting the ??=
and ??
operators to work below.
Conditional property access
To guard access to a property or method of an object that might be null, put a question mark (?
) before the dot (.
):
myObject?.someProperty
The preceding code is equivalent to the following:
(myObject != null) ? myObject.someProperty : null
You can chain multiple uses of ?.
together in a single expression:
myObject?.someProperty?.someMethod()
The preceding code returns null (and never calls someMethod()
) if either myObject
or myObject.someProperty
is null.
Code example
Try using conditional property access to finish the code snippet below.
Collection literals
Dart has built-in support for lists, maps, and sets. You can create them using literals:
final aListOfStrings = ['one', 'two', 'three'];
final aSetOfStrings = {'one', 'two', 'three'};
final aMapOfStringsToInts = {
'one': 1,
'two': 2,
'three': 3,
};
Dart’s type inference can assign types to these variables for you. In this case, the inferred types are List<String>
, Set<String>
, and Map<String, int>
.
Or you can specify the type yourself:
final aListOfInts = <int>[];
final aSetOfInts = <int>{};
final aMapOfIntToDouble = <int, double>{};
Specifying types is handy when you initialize a list with contents of a subtype, but still want the list to be List<BaseType>
:
final aListOfBaseType = <BaseType>[SubType(), SubType()];
Code example
Try setting the following variables to the indicated values.
Arrow syntax
You might have seen the =>
symbol in Dart code. This arrow syntax is a way to define a function that executes the expression to its right and returns its value.
For example, consider this call to the List
class’s any()
method:
bool hasEmpty = aListOfStrings.any((s) {
return s.isEmpty;
});
Here’s a simpler way to write that code:
bool hasEmpty = aListOfStrings.any((s) => s.isEmpty);
Code example
Try finishing the following statements, which use arrow syntax.
Cascades
To perform a sequence of operations on the same object, use cascades (..
). We’ve all seen an expression like this:
myObject.someMethod()
It invokes someMethod()
on myObject
, and the result of the expression is the return value of someMethod()
.
Here’s the same expression with a cascade:
myObject..someMethod()
Although it still invokes someMethod()
on myObject
, the result of the expression isn’t the return value — it’s a reference to myObject
! Using cascades, you can chain together operations that would otherwise require separate statements. For example, consider this code:
var button = querySelector('#confirm');
button.text = 'Confirm';
button.classes.add('important');
button.onClick.listen((e) => window.alert('Confirmed!'));
With cascades, the code becomes much shorter, and you don’t need the button
variable:
querySelector('#confirm')
..text = 'Confirm'
..classes.add('important')
..onClick.listen((e) => window.alert('Confirmed!'));
Code example
Use cascades to create a single statement that sets the anInt
, aString
, and aList
properties of a BigObject
to 1
, 'String!'
, and [3.0]
(respectively) and then calls allDone()
.
Getters and setters
You can define getters and setters whenever you need more control over a property than a simple field allows.
For example, you can make sure a property’s value is valid:
class MyClass {
int _aProperty = 0;
int get aProperty => _aProperty;
set aProperty(int value) {
if (value >= 0) {
_aProperty = value;
}
}
}
You can also use a getter to define a computed property:
class MyClass {
List<int> _values = [];
void addValue(int value) {
_values.add(value);
}
// A computed property.
int get count {
return _values.length;
}
}
Code example
Imagine you have a shopping cart class that keeps a private List<double>
of prices. Add the following:
- A getter called
total
that returns the sum of the prices - A setter that replaces the list with a new one, as long as the new list doesn’t contain any negative prices (in which case the setter should throw an
InvalidPriceException
).
Optional positional parameters
Dart has two kinds of function parameters: positional and named. Positional parameters are the kind you’re likely familiar with:
int sumUp(int a, int b, int c) {
return a + b + c;
}
// ···
int total = sumUp(1, 2, 3);
With Dart, you can make these positional parameters optional by wrapping them in brackets:
int sumUpToFive(int a, [int b, int c, int d, int e]) {
int sum = a;
if (b != null) sum += b;
if (c != null) sum += c;
if (d != null) sum += d;
if (e != null) sum += e;
return sum;
}
// ···
int total = sumUpToFive(1, 2);
int otherTotal = sumUpToFive(1, 2, 3, 4, 5);
Optional positional parameters are always last in a function’s parameter list. Their default value is null unless you provide another default value:
int sumUpToFive(int a, [int b = 2, int c = 3, int d = 4, int e = 5]) {
// ···
}
// ···
int newTotal = sumUpToFive(1);
print(newTotal); // <-- prints 15
Code example
Implement a function called joinWithCommas()
that accepts one to five integers, then returns a string of those numbers separated by commas. Here are some examples of function calls and returned values:
Function call | Returned value | |
---|---|---|
joinWithCommas(1) | ‘1’ | |
joinWithCommas(1, 2, 3) | ‘1,2,3’ | |
joinWithCommas(1, 1, 1, 1, 1) | ‘1,1,1,1,1’ |
Optional named parameters
Using a curly brace syntax, you can define optional parameters that have names.
void printName(String firstName, String lastName, {String suffix}) {
print('$firstName $lastName ${suffix ?? ''}');
}
// ···
printName('Avinash', 'Gupta');
printName('Poshmeister', 'Moneybuckets', suffix: 'IV');
As you might expect, the value of these parameters is null by default, but you can provide default values:
void printName(String firstName, String lastName, {String suffix = ''}) {
print('$firstName $lastName $suffix');
}
A function can’t have both optional positional and optional named parameters.
Code example
Add a copyWith()
instance method to the MyDataObject
class. It should take three named parameters:
int newInt
String newString
double newDouble
When called, copyWith()
should return a new MyDataObject
based on the current instance, with data from the preceding parameters (if any) copied into the object’s properties. For example, if newInt
is non-null, then copy its value into anInt
.
Exceptions
Dart code can throw and catch exceptions. In contrast to Java, all of Dart’s exceptions are unchecked exceptions. Methods don’t declare which exceptions they might throw, and you aren’t required to catch any exceptions.
Dart provides Exception
and Error
types, but you’re allowed to throw any non-null object:
throw Exception('Something bad happened.');
throw 'Waaaaaaah!';
Use the try
, on
, and catch
keywords when handling exceptions:
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');
}
The try
keyword works as it does in most other languages. Use the on
keyword to filter for specific exceptions by type, and the catch
keyword to get a reference to the exception object.
If you can’t completely handle the exception, use the rethrow
keyword to propagate the exception:
try {
breedMoreLlamas();
} catch (e) {
print('I was just trying to breed llamas!.');
rethrow;
}
To execute code whether or not an exception is thrown, use finally
:
try {
breedMoreLlamas();
} catch (e) {
// ... handle exception ...
} finally {
// Always clean up, even if an exception is thrown.
cleanLlamaStalls();
}
Code example
Implement tryFunction()
below. It should execute an untrustworthy method and then do the following:
- If
untrustworthy()
throws anExceptionWithMessage
, calllogger.logException
with the exception type and message (try usingon
andcatch
). - If
untrustworthy()
throws anException
, calllogger.logException
with the exception type (try usingon
for this one). - If
untrustworthy()
throws any other object, don’t catch the exception. - After everything’s caught and handled, call
logger.doneLogging
(try usingfinally
).
Using this in a constructor
Dart provides a handy shortcut for assigning values to properties in a constructor: use this.propertyName
when declaring the constructor:
class MyColor {
int red;
int green;
int blue;
MyColor(this.red, this.green, this.blue);
}
final color = MyColor(80, 80, 128);
This technique works for named parameters, too. Property names become the names of the parameters:
class MyColor {
...
MyColor({this.red, this.green, this.blue});
}
final color = MyColor(red: 80, green: 80, blue: 80);
For optional parameters, default values work as expected:
MyColor([this.red = 0, this.green = 0, this.blue = 0]);
// or
MyColor({this.red = 0, this.green = 0, this.blue = 0});
Code example
Add a one-line constructor to MyClass
that uses this.
syntax to receive and assign values for all three properties of the class.
Initializer lists
Sometimes when you implement a constructor, you need to do some setup before the constructor body executes. For example, final fields must have values before the constructor body executes. Do this work in an initializer list, which goes between the constructor’s signature and its body:
Point.fromJson(Map<String, num> json)
: x = json['x'],
y = json['y'] {
print('In Point.fromJson(): ($x, $y)');
}
The initializer list is also a handy place to put asserts, which run only during development:
NonNegativePoint(this.x, this.y)
: assert(x >= 0),
assert(y >= 0) {
print('I just made a NonNegativePoint: ($x, $y)');
}
Code example
Complete the FirstTwoLetters
constructor below. Use an initializer list to assign the first two characters in word
to the letterOne
and LetterTwo
properties. For extra credit, add an assert
to catch words of less than two characters.
Named constructors
To allow classes to have multiple constructors, Dart supports named constructors:
class Point {
num x, y;
Point(this.x, this.y);
Point.origin() {
x = 0;
y = 0;
}
}
To use a named constructor, invoke it using its full name:
final myPoint = Point.origin();
Code example
Give the Color class a constructor named Color.black
that sets all three properties to zero.
Factory constructors
Dart supports factory constructors, which can return subtypes or even null. To create a factory constructor, use the factory
keyword:
class Square extends Shape {}
class Circle extends Shape {}
class Shape {
Shape();
factory Shape.fromTypeName(String typeName) {
if (typeName == 'square') return Square();
if (typeName == 'circle') return Circle();
print('I don\'t recognize $typeName');
return null;
}
}
Code example
Fill in the factory constructor named IntegerHolder.fromList
, making it do the following:
- If the list has one value, create an
IntegerSingle
with that value. - If the list has two values, create an
IntegerDouble
with the values in order. - If the list has three values, create an
IntegerTriple
with the values in order. - Otherwise, return null.
Redirecting constructors
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 (:
).
class Automobile {
String make;
String model;
int mpg;
// The main constructor for this class.
Automobile(this.make, this.model, this.mpg);
// Delegates to the main constructor.
Automobile.hybrid(String make, String model) : this(make, model, 60);
// Delegates to a named constructor
Automobile.fancyHybrid() : this.hybrid('Futurecar', 'Mark 2');
}
Code example
Remember the Color
class from above? Create a named constructor called black
, but rather than manually assigning the properties, redirect it to the default constructor with zeros as the arguments.
Const constructors
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.
class ImmutablePoint {
const ImmutablePoint(this.x, this.y);
final int x;
final int y;
static const ImmutablePoint origin = ImmutablePoint(0, 0);
}
Code example
Modify the Recipe
class so its instances can be constants, and create a constant constructor that does the following:
- Has three parameters:
ingredients
,calories
, andmilligramsOfSodium
(in that order). - Uses
this.
syntax to automatically assign the parameter values to the object properties of the same name. - Is constant, with the
const
keyword just beforeRecipe
in the constructor declaration.
What next?
We hope you enjoyed using this codelab to learn or test your knowledge of some of the most interesting features of the Dart language. Here are some suggestions for what to do now:
- Try other Dart codelabs.
- Read the Dart language tour.
- Play with DartPad.
- Get the Dart SDK.