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 8printInteger(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
nullare objects. All objects inherit from theObjectclass. -
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
varfor local variables instead of type annotations. -
The default value for uninitialized variables is
null.1 2int lineCount; assert(lineCount == null); -
finalandconst:-
A
finalvariable can be set only once. Usefinalif we don’t know the value at compile time.1 2final name = 'Bob'; // Without a type annotation final String nickname = 'Bobby'; // Or this -
Use
constfor variables that are compile-time constants. If it’s at the class level, make itstatic const. We can also useconstfor 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 4var foo = const []; final bar = const []; const baz = []; // Equivalent to `const []` foo = [1, 2, 3]; // Was const []
-
Built-in Types
Numbers
-
Both
intanddoubleare subtypes ofnum. -
Conversion between a string and a number:
1 2 3 4int 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 2var 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
trueandfalsehave the typebool, which means we have to check the values explicitly (unlike Python).1 2var fullName = ''; assert(fullName.isEmpty);
Lists
-
Example:
1 2 3 4var 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 4var 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 11var 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 3candidates .where((c) => c.yearsOfExperience >= 5) .forEach((c) => c.interview());
Sets
-
Example:
1 2 3 4 5 6 7var 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 10var 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
nullif 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 4import 'package:characters/characters.dart'; var hi = 'Hi 🇩🇰'; print('The las character: ${hi.characters.last}');
Symbols
-
A
Symbolobject 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 9bool 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 3querySelector('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 3void enableFlags({bool bold, bool hidden}) {...} enableFlags(bold: true, hidden: false); -
Although named parameters are a kind of optional parameter, we can used
@requiredto make it mandatory:1const Scrollbar({Key key, @required Widget child})
Positional parameters
-
Wrapping a set of parameters in
[]makes them optional positional:1String say(String from, String msg, [String device]) {...}
Anonymous Functions
-
Example:
1 2 3 4 5 6 7var 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'isTrue if the object has the type.is!False if the object has the type.
-
b ??= valueassigns 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?.baris null if foo is null.
Exceptions
-
Example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15throw 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 5class Point { double x, y; Point(this.x, this.y); } -
Named constructors for extra clarity:
1 2 3 4Point.origin() { x = 0; y = 0; } -
Invoke the super constructor almost like in C++:
1 2 3Employee.fromJson(Map data) : super.fromJson(data) { ... } -
Redirecting constructors:
Point.alongXAxis(double x) : this(x, 0)delegates to the main constructor. -
Use
factoryfor 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 23abstract 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 3abstract 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 theinterfacekeyword.) -
Liked in C++, operators can be overriden:
1Vector 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 7class 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 4enum Color {red, green, blue} assert(Color.red.index == 0); List<Color> colors = Color.values;
Asynchrony
-
To use
await, code must be in anasyncfunction:1 2 3 4 5Future 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 12String 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); }