Dart Notes
- Introduction
- Variables
- Built-in Types
- Functions
- Operators
- Exceptions
- Classes
- Enum
- Asynchrony
- Functional Programming
- References
Introduction
-
A basic Dart program:
1 2 3 4 5 6 7 8
printInteger(int aNumber) { print('The number is $aNumber'); } main() { var number = 42; printInteger(number); }
-
Everything that can be placed in a variable is an object. Even numbers, function, and
null
are objects. All objects inherit from theObject
class. -
Dart is strongly typed. But type annotations are optional thanks to type inference. When we want to say explicitly that no type is expected, use the type
dynamic
. -
Dart supports generic types like
List<int>
orList<dynamic>
(a list of objects of any type). -
Unlike Java, Dart doesn’t have
public
,protected
, andprivate
. Prefix an underscore_
makes it private to the library. It generally means that the identifier is visible only inside the file (not just the class).
Variables
-
Use
var
for local variables instead of type annotations. -
The default value for uninitialized variables is
null
.1 2
int lineCount; assert(lineCount == null);
-
final
andconst
:-
A
final
variable can be set only once. Usefinal
if we don’t know the value at compile time.1 2
final name = 'Bob'; // Without a type annotation final String nickname = 'Bobby'; // Or this
-
Use
const
for variables that are compile-time constants. If it’s at the class level, make itstatic const
. We can also useconst
for constant values. We can change the value of a non-final, non-const variable, even if it used to have a const value.1 2 3 4
var foo = const []; final bar = const []; const baz = []; // Equivalent to `const []` foo = [1, 2, 3]; // Was const []
-
Built-in Types
Numbers
-
Both
int
anddouble
are subtypes ofnum
. -
Conversion between a string and a number:
1 2 3 4
int one = int.parse('1'); double onePointOne = double.parse('1.1'); String oneAsString = 1.toString(); String piAsString = 3.14159.toStringAsFixed(2); // 3.14
Strings
-
Both single or double quotes are fine. It’s easy to escape the delimiter:
'It\'s easy.'
-
String interpolation:
${expression}
. If the expression is an identifier, we can skip{}
.1 2
var s = 'string interpolation'; print('Dart has $s');
-
Like in Python, create a multi-line string using triple quote
'''
or"""
. -
Strings use UTF-16.
Booleans
-
Only
true
andfalse
have the typebool
, which means we have to check the values explicitly (unlike Python).1 2
var fullName = ''; assert(fullName.isEmpty);
Lists
-
Example:
1 2 3 4
var list = [1, 2, 3]; assert(list.length == 3); var constantList = const [1, 2, 3]; // constantList[1] = 1; // Uncommenting this causes an error.
-
Spread operator (
...
) and null-aware spread operator (....?
) provide a concise way to insert all elements into a collection:1 2 3 4
var list = [1, 2, 3]; var list2 = [0, ...list]; // [0, 1, 2, 3] var list3; var list4 = [0, ...?list3]; // list3 might be null. Avoid exceptions.
-
Collection if and collection for (similar to list comprehension in Python):
1 2 3 4 5 6 7 8 9 10 11
var nav = [ 'Home', 'Furniture', 'Plants', if (promoActive) 'Outlet' ]; var listOfInts = [1, 2, 3]; var listOfStrings = [ '#0', for (var i in listOfInts) `#$i` ];
-
forEach()
can replace a for loop;where()
can be used to filter:1 2 3
candidates .where((c) => c.yearsOfExperience >= 5) .forEach((c) => c.interview());
Sets
-
Example:
1 2 3 4 5 6 7
var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'}; var names = <String>{}; // Empty set // Set<String> names = {}; // Works too // var names = {}; // Creates a map, not a set. var elements = <String>{}; elements.add('flourine'); elements.addAll(halogens);
Maps
-
Example:
1 2 3 4 5 6 7 8 9 10
var nobleGases = { 2: 'helium', 10: 'neon', 18: 'argon' }; // Or we can use the constructor var nobleGases = Map(); // The new keyword is optional nobleGases[2] = 'helium'; nobleGases[10] = 'neon'; nobleGases[18] = 'argon';
-
The map returns a
null
if the key doesn’t exist.
Runes and Grapheme Clusters
-
In Dart, runes expose the Unicode code points of a string.
-
Because a Dart string is a sequence of UTF-16 code units, the usual way to express a code point is
\uXXXX
, where XXXX is a 4-digit hexadecimal value. For more or less than 4 hex digits, place the value in curly brackets.1 2 3 4
import 'package:characters/characters.dart'; var hi = 'Hi 🇩🇰'; print('The las character: ${hi.characters.last}');
Symbols
-
A
Symbol
object represents an operator or identifier declared in a Dart program. We 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.1 2
#radix #bar
Functions
-
Dart is a true object-oriented language. Even functions are objects and have a type.
-
Although Effective Dart recommends type annotations for public APIs, the function still works if you omit the types.
1 2 3 4 5 6 7 8 9
bool isNoble(int atomicNumber) { return _nobleGases[atomicNumber] != null; } // Or the below isNoble(atomicNumber) { return _nobleGases[atomicNumber] != null; } // Or the arrow syntax bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
-
..
is a cascade, which enables us to perform multiple operations on the members of a single object:1 2 3
querySelector('id') ..text = 'Click me!' ..onClick.listen(reverseText);
-
All functions return a value. If no return value is specified, it returns
null
.
Optional Parameters
- Optional parameters can be either named or positional, but not both.
Named Parameters
-
Example:
1 2 3
void enableFlags({bool bold, bool hidden}) {...} enableFlags(bold: true, hidden: false);
-
Although named parameters are a kind of optional parameter, we can used
@required
to make it mandatory:1
const Scrollbar({Key key, @required Widget child})
Positional parameters
-
Wrapping a set of parameters in
[]
makes them optional positional:1
String say(String from, String msg, [String device]) {...}
Anonymous Functions
-
Example:
1 2 3 4 5 6 7
var list = ['apples', 'bananas', 'oranges']; list.forEach((item) { print('${list.indexOf(item)}: $item'); }); // Or the arrow notation list.forEach( (item) => print('${list.indexOf(item)}: $item'));
Operators
-
~/
is the floor division. The rest are the same as the ones in Java. -
as
,is
, andis!
:as
: Typecast. Use it iff we are sure the object is of that type:(emp as Person).firstName = 'Bob'
is
True if the object has the type.is!
False if the object has the type.
-
b ??= value
assigns if b is null; otherwise b stays the same. -
String playerName(String name) => name ?? 'Guest';
gives name'Guest'
if name is null. -
?.
conditional access like in Swift/Kotlin.foo?.bar
is null if foo is null.
Exceptions
-
Example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
throw FormatException('Expected at least 1 section'); try { breadMoreLlamas(); } on OutOfLlamasException { buyMoreLlamas(); } on Exception catch (e, s) { print('Unknown exception: $e'); print('Stack trace $s'); } catch (e) { // handles all print('Something really unknown: $e'); rethrow; // Allow callers to see the exception. } finally { // Always executes. }
Classes
-
All instance variables generate an implicit getter method. Non-final instance variables also generate an implicit setter. Just do
foo.x
. -
Constructor syntactic sugar:
1 2 3 4 5
class Point { double x, y; Point(this.x, this.y); }
-
Named constructors for extra clarity:
1 2 3 4
Point.origin() { x = 0; y = 0; }
-
Invoke the super constructor almost like in C++:
1 2 3
Employee.fromJson(Map data) : super.fromJson(data) { ... }
-
Redirecting constructors:
Point.alongXAxis(double x) : this(x, 0)
delegates to the main constructor. -
Use
factory
for a constructor that doesn’t always create a new instance of the class. It may return an instance from a cache or return an instance of a subtype.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
abstract class Shape { factory Shape(String type) { if (type == 'circle') return Circle(2); if (type == 'square') return Square(2); throw "Can't create $type."; } num get area; } class Circle implements Shape { final num radius; Circle(this.radius); num get area => pi * pow(radius, 2); } class Square implements Shape { final num side; Square(this.side); num get area => pow(side, 2); } final circle = Shape('circle'); final square = Shape('square');
-
Getters and setters:
1 2 3 4 5 6
// We can define explicit getters and setters using get and set int _speed = 0; get speed => _speed; // Read-only double get right => left + width; set right(double value) => left = value - width
-
Abstract classes and methods:
1 2 3
abstract class AbstractContainer { void updateChildren(); // Abstract method }
-
Every class implicitly defines an interface containing all instance members of the class and of any interfaces it implements. Like in Java, we have
extends
,implements
, and@override
. (But Dart doesn’t have theinterface
keyword.) -
Liked in C++, operators can be overriden:
1
Vector operator +(Vector v) => Vector(x + v.x, y + v.y);
-
Mixins: a way of reusing a class’s code in multiple class hierarchies. Use
with
:1 2 3 4 5 6 7
class Musician extends Performer with Musical { ... } // To implement, declare no constructors mixin Musical { // mixin Musical on Musician to restrict the types that can use the mixin ... }
Enum
-
Example:
1 2 3 4
enum Color {red, green, blue} assert(Color.red.index == 0); List<Color> colors = Color.values;
Asynchrony
-
To use
await
, code must be in anasync
function:1 2 3 4 5
Future checkVersion() async { // It returns a Future object var version = await lookUpVersion(); } // Future is then-able, so can always say checkVersion().then((returnValue) => foo());
-
Use
await for ()
to handle a Stream (wait for all of the streams results). But we should not use this for UI event listeners, because UI frameworks send endless streams of events.
Functional Programming
-
Example:
1 2 3 4 5 6 7 8 9 10 11 12
String scream(int length) => "A${'a' * length}h!"; main() { final values = [1, 2, 3, 5, 10, 50]; for (var length in values) { print(scream(length)); } // Is the same as values.map(scream).forEach(print); // More values.skip(1).take(3).map(scream).forEach(print); }