General Dart Flashcards
What is this operator called and what does it do?
??=
E.g.
a ??= 3;
Null Assignment operator
If the value to the left is null then it will assign it the value to the right. E.g.
int? a; // = null
a ??= 3;
print(a); // Prints 3
What is this operator called and what does it do?
??
E.g.
print(1 ?? 3);
Null Aware operator
It will return the value on the left, however if that value is null then it will return the value on the right.
E.g.
print(1 ?? 3); // Prints ‘1’
print(null ?? 12); // Prints ‘12’
What is this operator called and what does this operator do?
?.
E.g.
obj?.method()
Null Conditional Member Access operator
If the object on the left is null then null will be returned, however if it is not null then the result of the method (or property getter) will be returned.
Use ?. when you want to call a method/getter on an object IFF that object is not null (otherwise, return null).
What is this operator called and what does it do?
…?
Null Spread operator
Placing … before an expression inside a collection literal unpacks the result of the expression and inserts its elements directly inside the new collection. If the expression is null then it will be ignored.
List lowerNumbers = [1, 2, 3, 4, 5];
List? upperNumbers = null;
List numbers = […lowerNumbers, …?upperNumbers];
numbers.forEach(print); // Prints ‘1 2 3 4 5’
4 reasons to use getters and setters…
- Read-only access
- Write only access
- Verify on set
- Compute a value before set
What is the format of the single-line ternary operator?
aBoolean ? isTrue : ifFalse;
What is this operator and what does it do?
.. (2 dots)
Also is there a null-safety version?
Cascade operator
It perform a sequence of operations on the same object.
E.g.
BigObject fillBigObject(BigObject obj) { return obj ..anInt = 1 ..aString = 'String!' ..aList = [3.0] ..allDone(); }
Note that the return value of each operation is ignored and after all of the operations have completed the cascade will return the original object that is being operated on. In this case… obj.
The null-safety version is ?.. (null-shorting) and it will only perform the cascading operations if the object is not null. You only use it on the first operator and the rest are all normal ones. E.g.
result1
?..doStuff()
..doMoreStuff();
What are these two operators:
- .. (2 dots)
- … (3 dots)
- .. (2 dots) The cascade operator. It’s for chaining multiple operations on the same object. It returns the object itself.
- … (3 dots) The Spread operator. It’s for unpacking the result of an expression into a collection literal definition.
When do we need to use initialiser lists?
All final final fields must be initialised in the initialiser list section of a class’ constructor. They can’t even be used in the constructors body until they have been initialised. The initialiser list runs before the constructor’s body.
What is the purpose of assert() calls?
To find errors during debugging.
Asserts will not be run during production.
They can also be used to run specific logic during debug builds only. See https://stackoverflow.com/questions/56537718/what-assert-do-in-dart
How do you lazily initialise a field?
Use the late modifier and provide a function call as the return value.
E.g.
class Weather { late int _temperature = _readThermometer(); }
Instead of running it as soon as the instance is constructed, it is deferred and run lazily the first time the field is accessed.
The function (in this case _readThermometer) is called only on the first time. This appears to make a lazy initialised field final by nature.
A constructor call can also be used to late initialise objects. e.g. class Weather { late TempSensor _tempSensor = TempSensor(); }
Why would you use lazy initialisation?
When the initialisation expression is costly and may not be needed.
How can you access ‘this’ when initialising fields?
Usually instance field initialisers cannot access ‘this’ because you don’t have access to the new object until all field initialisers have completed. But with a late field, that’s no longer true, so you can access ‘this’, call methods, or access fields on the instance.
When is it okay to use ‘var’ instead of explicitly declaring the type?
When they are local variables in small functions with very little scope. If the function is large enough that the reader must scroll then the full type should be declared instead.
Due to the small scope size, the reader does not have to scroll to where the variable is initialised to see the declared type. The initialisation code will always be visible.
Omitting the type focuses the reader’s attention on the more important name of the variable and its initialised value.
This will be most beneficial when you have long Types cluttering the code. E.g. List>
What does the asterisk mean in async* and sync*?
It makes that function a ‘Generator Function’.