Dart in half an hour
what you need to know in order to read it
What it is
Dart resembles Java, JavaScript and TypeScript at the same time. If you have
read chapter 9, half of it is already familiar: types, async/await,
classes.
This article covers what differs and what turns up in every file of a Flutter project.
What it looks like in code
class City {
final String name;
final double? temp;
const City({required this.name, this.temp});
String get report => temp == null
? '$name: no data'
: '$name: ${temp! > 0 ? '+' : ''}$temp°';
}
void main() {
final cities = <City>[
const City(name: 'Oslo', temp: 4),
const City(name: 'Tromsø'),
];
for (final c in cities) {
print(c.report);
}
}What matters here
final instead of const and let. final means assign once. const
means the value is known at compile time. In Flutter const turns up
constantly: it lets widgets avoid being recreated on a redraw.
double? temp — a type with a question mark, the same as
number | null in TypeScript. Dart is as strict about emptiness as
TypeScript with strict: you can't reach for temp directly until you
have checked.
temp! — the assertion "there is definitely something here". The direct
analogue of as from article 9.5 and the same source of trouble: it is a
promise to the compiler, not a check. Get it wrong and the application dies
with Null check operator used on a null value.
required — a mandatory named parameter. Without it the argument can be
left out.
$name and ${expression} — interpolation into a string, like
f-strings in Python.
get report => ... — a computed property: accessed like a field,
without parentheses.
Named parameters
The main syntactic difference you have to get used to:
City(name: 'Oslo', temp: 4) // named, in curly bracesIn Flutter nearly every parameter is named, and that explains why the code looks so verbose:
Padding(
padding: const EdgeInsets.all(16),
child: Text('Hello'),
)The upside is that while reading you can always see what each value means.
The rest, briefly
| Written as | What it means |
|---|---|
late | "there will be a value, but later" — a promise, checked at run time |
?. | safe access, as in JS (article 9.1) |
?? | a default value |
.. | a cascade: several operations on one object |
=> | the short form of a one-expression function |
List<City> | a list of cities; <> as in generics (article 9.4) |
late is worth a separate mention. It is a way of saying "I will assign
this later". If you didn't assign it and then reach for it, you get a
run-time error. An AI uses late to get around the initialization
requirement — and that is the same gag as as and !.
What to tell the AI
Explain this class line by line: what final means, what
required means, what the exclamation mark after temp does.This uses `late` and `!`. Rewrite it without them, with
proper null checks. If there's a place where they can't be
avoided — explain why instead of rewriting it.Write a data model for this API's response: here is a sample
JSON. Make fields that may be absent honestly nullable.
Add a fromJson factory with checks rather than casts.What to look for
! at every turn. The application will die wherever the data turned out
not to be there.
late instead of proper initialization. The same problem, only later.
Code for old Flutter versions. Outdated widget and parameter names.
dynamic. The exact analogue of any from TypeScript: it switches
checking off.
JSON parsing with no checks. json['main']['temp'] as double fails if
the server answered with an error — exactly as in articles 2.12 and 9.5.
Cheat sheet
finalmeans assign once,constmeans known at compile time.double?may be empty; Dart won't let you reach for it unchecked.!,lateand a cast switch off the emptiness check.Parameters in Flutter are nearly always named:
City(name: ..., temp: ...).$nameand${expression}interpolate into a string.dynamicisany, with all that follows.