Dart is the language behind Flutter. This reference covers the patterns you’ll use every day.
Variables and types
// Immutable (prefer this)
final name = 'Alice'; // type inferred, set once
final int age = 30; // explicit type
// Compile-time constant
const pi = 3.14159;
const defaultTimeout = Duration(seconds: 30);
// Mutable
var count = 0; // type inferred
String greeting = 'hello'; // explicit type
// Nullable (Dart null safety)
String? maybeNull; // can be null
int? maybeInt; // can be null
String alwaysValue = 'hello'; // cannot be null
// Late initialization
late String description; // will be set before first use
late final ThemeData theme; // will be set onceCollections
// List
var fruits = ['apple', 'banana', 'cherry']; // List<String>
fruits.add('date');
fruits.remove('banana');
fruits[0]; // 'apple'
fruits.length; // 3
fruits.isEmpty;
fruits.contains('apple'); // true
var nums = [1, 2, 3, 4, 5];
nums.where((n) => n > 2); // (3, 4, 5) - Iterable
nums.map((n) => n * 2); // (2, 4, 6, 8, 10)
nums.reduce((a, b) => a + b); // 15
nums.fold(0, (acc, n) => acc + n); // 15
nums.any((n) => n > 4); // true
nums.every((n) => n > 0); // true
nums.forEach((n) => print(n));
var evens = nums.where((n) => n.isEven).toList(); // [2, 4]
// Set
var set = <String>{'apple', 'banana'};
set.add('cherry');
set.contains('apple'); // true
// Map
var scores = {'Alice': 95, 'Bob': 87}; // Map<String, int>
scores['Charlie'] = 92;
scores['Alice']; // 95
scores.containsKey('Bob'); // true
scores.keys; // ('Alice', 'Bob', 'Charlie')
scores.values; // (95, 87, 92)
scores.forEach((k, v) => print('$k: $v'));
// Spread and collection-if
var combined = [...fruits, ...['date', 'elderberry']];
var positives = nums.where((n) => n > 0).toList();
var withHeader = ['Header', if (showItems) ...items];Functions
// Named parameters (prefer for clarity)
void createUser({required String name, int age = 0, String? email}) {
print('$name, $age, $email');
}
createUser(name: 'Alice', age: 30);
// Positional parameters
String greet(String name, [String? title]) {
return 'Hello, ${title ?? ""} $name';
}
greet('Alice'); // 'Hello, Alice'
greet('Alice', 'Dr.'); // 'Hello, Dr. Alice'
// Arrow functions
int square(int n) => n * n;
bool isAdult(int age) => age >= 18;
// Typedef for function types
typedef Validator = String? Function(String? value);Classes
class User {
final String name;
final int age;
String? email;
// Constructor
User({required this.name, required this.age, this.email});
// Named constructor
User.guest() : name = 'Guest', age = 0;
// Factory constructor
factory User.fromJson(Map<String, dynamic> json) {
return User(
name: json['name'] as String,
age: json['age'] as int,
email: json['email'] as String?,
);
}
// Method
String greet() => 'Hi, I\'m $name';
// Override
@override
String toString() => 'User($name, $age)';
// Getter
bool get isAdult => age >= 18;
}
// Abstract class
abstract class Repository {
Future<List<User>> getAll();
Future<User> getById(String id);
}
// Implementing (implements)
class InMemoryRepository implements Repository {
final List<User> _users = [];
@override
Future<List<User>> getAll() async => _users;
@override
Future<User> getById(String id) async =>
_users.firstWhere((u) => u.name == id);
}
// Extending (inherits)
class Admin extends User {
final List<String> permissions;
Admin({required String name, required int age, this.permissions = const []})
: super(name: name, age: age);
}
// Mixin — share behavior across classes
mixin Loggable {
void log(String message) => print('[${DateTime.now()}] $message');
}
class Service with Loggable {
void doWork() {
log('Starting work');
}
}Enums
enum Status { pending, active, completed }
// Enhanced enums (Dart 2.17+)
enum Weather {
sunny(icon: '☀️', temp: 30),
cloudy(icon: '☁️', temp: 20),
rainy(icon: '🌧️', temp: 15);
final String icon;
final int temp;
const Weather({required this.icon, required this.temp});
}
// Pattern matching on enums
String describe(Status s) => switch (s) {
Status.pending => 'Waiting...',
Status.active => 'In progress',
Status.completed => 'Done!',
};Async / Await
// Future — a value available later
Future<String> fetchName() async {
await Future.delayed(Duration(seconds: 1));
return 'Alice';
}
// Multiple futures in parallel
final results = await Future.wait([
fetchUsers(),
fetchPosts(),
]);
// Streams — a sequence of async values
Stream<int> countStream(int max) async* {
for (int i = 1; i <= max; i++) {
await Future.delayed(Duration(seconds: 1));
yield i;
}
}
// Listen to a stream
final subscription = countStream(5).listen(
(value) => print(value),
onError: (e) => print('Error: $e'),
onDone: () => print('Done'),
);
// await for
await for (final value in countStream(3)) {
print(value);
}Null safety patterns
// Safe call
name?.length; // null if name is null
// Null coalesce
final display = name ?? 'Unknown';
// Null assertion (use sparingly)
final length = name!.length; // throws if null
// Conditional property access
list?.length ?? 0; // 0 if list is null
// Null-aware spread
var items = [...?nullableList]; // spreads if not null, empty otherwise
// Null-aware cascade
object?..method()..anotherMethod();Pattern matching (Dart 3)
// Switch expressions
String describe(Object value) => switch (value) {
int() => 'integer: $value',
String() => 'string: $value',
List() => 'list with ${value.length} items',
_ => 'unknown',
};
// Destructuring
class Point(final double x, final double y);
void printPoint(Point p) {
var (:x, :y) = p;
print('($x, $y)');
}
// Map pattern
if (json case {'name': String name, 'age': int age}) {
print('$name is $age');
}
// List pattern
if (items case [first, ...rest]) {
print('First: $first, Rest: $rest');
}