Dart in 10 mins
Dart is the language Flutter is written in: sound null safety, strong types, JIT for dev / AOT for release, async/await, isolates for parallel work. Modern Java-adjacent syntax with functional flourishes.
Variables, functions, classes, async
EXAMPLE
// 1) Variables + type inference
void main() {
int count = 42; // explicit
var name = 'Ada'; // String — inferred
final email = 'a@x.com'; // immutable, runtime
const pi = 3.14159; // compile-time constant
String? maybe = null; // nullable (sound null safety)
late int delayedInit; // promised non-null later
delayedInit = 100;
print(name);
}
// 2) Strings
var greeting = 'Hello, $name!';
var multi = '''multi-line
string''';
var raw = r'Raw \\n is literal';
var runes = '🇦🇺'.runes.toList();
var parsed = int.parse('42');
var formatted = '${name.toUpperCase()}-${count.toString().padLeft(5, '0')}';
// 3) Numbers, lists, maps
int i = 42;
double d = 3.14;
List<int> nums = [1, 2, 3, 4, 5];
Map<String, int> scores = {'Ada': 95, 'Bo': 88};
Set<String> tags = {'vip', 'beta'};
// Spread, collection if/for
var moreNums = [...nums, 6, 7];
var conditional = [
'always',
if (isLoggedIn) 'profile',
for (var i = 1; i <= 3; i++) 'item-$i',
];
// 4) Null safety operators
String? name;
name?.toUpperCase(); // safe call
name ?? 'Anonymous'; // default
name ??= 'Default'; // assign if null
name!.length; // assert non-null (THROWS if null)
// 5) Functions
int add(int a, int b) => a + b; // arrow function
int multiply(int a, int b) { // block body
return a * b;
}
// Optional + named parameters
String greet(String name, {String greeting = 'Hello', bool loud = false}) {
final s = '$greeting, $name';
return loud ? s.toUpperCase() : s;
}
greet('Ada');
greet('Bo', greeting: 'Hi', loud: true);
// Required named parameters (recommended for readability)
User createUser({required String email, required String name, String? phone}) {
return User(email: email, name: name, phone: phone);
}
// 6) Anonymous + closures
var nums2 = [1, 2, 3, 4, 5];
var doubled = nums2.map((n) => n * 2).toList();
var evens = nums2.where((n) => n.isEven).toList();
var sum = nums2.reduce((a, b) => a + b);
var makeAdder = (int x) => (int y) => x + y;
var add5 = makeAdder(5);
print(add5(10)); // 15
// 7) Classes
class User {
final String name;
final String email;
int age;
User({required this.name, required this.email, this.age = 0});
// Named constructor
User.guest() : name = 'Guest', email = '', age = 0;
User.fromMap(Map<String, dynamic> m) :
name = m['name'],
email = m['email'],
age = m['age'] ?? 0;
// Method
String greet() => 'Hi, $name';
// Getter
bool get isAdult => age >= 18;
// Setter
set ageInMonths(int months) => age = (months / 12).floor();
@@override
String toString() => 'User(name: $name, email: $email)';
}
// Usage
var u = User(name: 'Ada', email: 'a@x.com', age: 32);
u.greet();
print(u.isAdult);
// 8) Inheritance, interfaces, mixins
abstract class Animal {
String get name;
String sound();
void describe() => print('$name says ${sound()}');
}
class Dog extends Animal {
@@override final String name;
Dog(this.name);
@@override String sound() => 'woof';
}
// Interface — Dart has no interface keyword; any class can be implemented
class Loud implements Animal {
@@override String get name => 'Loud';
@@override String sound() => 'WOOF!';
@@override void describe() => print('Loud one');
}
// Mixin
mixin Swimmer {
void swim() => print('Swimming…');
}
class Duck extends Animal with Swimmer {
@@override final String name = 'Donald';
@@override String sound() => 'quack';
}
final d = Duck();
d.describe();
d.swim();
// 9) Records (Dart 3+) — anonymous tuples
var coords = (lat: -33.86, lng: 151.21);
print(coords.lat); // -33.86
(String, int) parse(String s) {
final parts = s.split(':');
return (parts[0], int.parse(parts[1]));
}
var (name, port) = parse('localhost:8080');
// 10) Pattern matching (Dart 3+)
String describe(Object o) => switch (o) {
int i when i < 0 => 'negative $i',
int i => 'positive $i',
String s => 'string "$s"',
[int x, int y] => 'list of two ints',
{'name': var n} => 'map with name $n',
_ => 'unknown',
};
// Destructure
final user = {'name': 'Ada', 'age': 32};
if (user case {'name': String name, 'age': int age}) {
print('$name, $age');
}
// 11) Async + await
Future<String> fetchUserName() async {
final response = await http.get(Uri.parse('https://api.example.com/me'));
final data = jsonDecode(response.body);
return data['name'];
}
void main() async {
final name = await fetchUserName();
print(name);
}
// Parallel — Future.wait
final results = await Future.wait([
fetchUser(1),
fetchUser(2),
fetchUser(3),
]);
// Try / catch
try {
final body = await fetchData();
process(body);
} on http.ClientException catch (e) {
print('network: $e');
} catch (e, stack) {
print('unexpected: $e\n$stack');
} finally {
cleanup();
}
// 12) Streams — async sequences
Stream<int> count(int n) async* {
for (var i = 0; i < n; i++) {
await Future.delayed(Duration(seconds: 1));
yield i;
}
}
await for (final n in count(5)) {
print(n);
}
// 13) Isolates — true parallelism
import 'dart:isolate';
int heavyCompute(int n) {
int total = 0;
for (var i = 0; i < n; i++) total += i;
return total;
}
Future<int> runInIsolate(int n) async {
return await Isolate.run(() => heavyCompute(n));
}
// 14) Enums — first-class with methods (Dart 2.17+)
enum Status {
active(label: 'Active', code: 'A'),
paused(label: 'Paused', code: 'P'),
archived(label: 'Archived', code: 'X');
final String label;
final String code;
const Status({required this.label, required this.code});
bool get isAlive => this != archived;
}
print(Status.active.label); // 'Active'
print(Status.active.isAlive); // true
// 15) Extension methods — add to existing types
extension StringExt on String {
bool get isValidEmail => RegExp(r'^[^@@]+@@[^@@]+\.[^@@]+$').hasMatch(this);
String get capitalised => this[0].toUpperCase() + substring(1);
}
print('Ada'.capitalised); // 'Ada'
print('a@x.com'.isValidEmail); // true
// 16) Sealed classes (Dart 3+) — pattern matching for closed hierarchies
sealed class Result<T> {}
class Success<T> extends Result<T> { final T data; Success(this.data); }
class Failure extends Result<Never> { final String error; Failure(this.error); }
class Loading extends Result<Never> {}
String handle(Result<int> r) => switch (r) {
Success(:final data) => 'Got $data',
Failure(:final error) => 'Error: $error',
Loading() => 'Loading…',
};
// 17) Common bugs
// • Forgetting required keyword → null at runtime where compiler couldn't catch
// • Using ! when value could be null → runtime exception
// • Mutating final variable → compile error
// • Mixing types in collections → use explicit types
// • Not awaiting Future → silent unhandled
// • Stream subscriptions not cancelled → leaks
// 18) Best practices
// ✅ Use sound null safety (Dart 2.12+, default in 3.0)
// ✅ Prefer final over var; const for compile-time constants
// ✅ Required named parameters for readability
// ✅ Records + pattern matching for structured data
// ✅ Sealed classes for discriminated unions
// ✅ Isolates for CPU-bound parallelism
// ✅ Extension methods to add behaviour without subclassing
// ✅ async/await for sequential async; Future.wait for parallel
Why it matters
Modern Dart (3+) gives you records, pattern matching, sealed classes, sound null safety, and isolates — on par with Kotlin and Swift for ergonomics. Use named required parameters, final by default, and pattern matching for type-safe data handling.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Exercise
String interpolation in Dart.
final s = 'Hello,
name';
One character.
Discussion
Loading…