Dart Cheatsheet

Classes, null safety, async, collections, mixins, generics, patterns & Flutter essentials

Language
Contents

Basics & Types

void main() {
  print('Hello, Dart!');
}

// Variables
var name = 'Alice';       // type inferred
String city = 'NYC';      // explicit type
final age = 30;           // runtime constant
const pi = 3.14;          // compile-time constant
late String description;   // lazy initialization

// Types: int, double, String, bool, List, Map, Set, dynamic, num
int x = 42;
double y = 3.14;
num z = 42;               // int or double
bool flag = true;
dynamic anything = 42;    // any type (avoid when possible)

// Null safety (sound by default)
String? nullable;         // can be null
String nonNull = 'hi';   // cannot be null
var len = nullable?.length ?? 0;   // null-aware access + default
var val2 = nullable!;              // assert non-null
nullable ??= 'default';            // assign if null

// String interpolation
var msg = 'Hello $name, age ${age + 1}';
var multi = '''
  Multi-line string
''';
var raw = r'No \n escaping here';

// Type checks
if (obj is String) print(obj.length);   // auto-promotes
if (obj is! int) print('not an int');
var str = obj as String;              // explicit cast

// Typedef
typedef IntList = List<int>;
typedef Compare<T> = int Function(T a, T b);
🔀

Control Flow

// if / else
if (score >= 90) {
  print('A');
} else if (score >= 80) {
  print('B');
} else {
  print('C');
}

// Conditional expression
var label = score >= 90 ? 'A' : 'B';

// switch (classic)
switch (command) {
  case 'start':
    start();
    break;
  case 'stop':
  case 'pause':
    stop();
    break;
  default:
    print('unknown');
}

// Switch expression (Dart 3+)
var result = switch (value) {
  1 => 'one',
  2 || 3 => 'two or three',
  >= 4 && <= 10 => '4-10',
  _ => 'other',
};

// for loops
for (var i = 0; i < 5; i++) print(i);
for (var item in list) print(item);
list.forEach((item) => print(item));

// while / do-while
while (x > 0) { x--; }
do { x++; } while (x < 10);

// Labeled loops
outer:
for (var i = 0; i < 5; i++) {
  for (var j = 0; j < 5; j++) {
    if (j == 3) break outer;
  }
}

// assert (debug only)
assert(age >= 0, 'Age must be non-negative');
🔧

Functions

// Basic function
int add(int a, int b) => a + b;  // arrow syntax

String greet(String name) {
  return 'Hello $name';
}

// Optional positional parameters
String say(String from, String msg, [String? device]) {
  var result = '$from says $msg';
  if (device != null) result = '$result via $device';
  return result;
}

// Named parameters (can be required)
void createUser({required String name, int age = 0}) {}
createUser(name: 'Alice', age: 25);

// First-class functions
var multiply = (int a, int b) => a * b;
Function makeAdder(int n) => (int i) => n + i;
var add2 = makeAdder(2);
add2(3);  // 5 — closure

// Higher-order functions
void execute(int Function(int, int) op) {
  print(op(3, 4));
}
execute((a, b) => a + b);

// Tear-offs (function references)
list.forEach(print);                // top-level function
list.map(element.toString);         // instance method
list.where(int.parse != null);     // static method

// Cascade notation (..)
var paint = Paint()
  ..color = Colors.red
  ..strokeWidth = 5.0
  ..style = PaintingStyle.stroke;
📦

Collections

// List
var list = <int>[1, 2, 3];
list.add(4);  list.remove(2);  list.length;
list.insert(0, 99);  list.removeAt(0);
list.where((n) => n > 2).toList();
list.map((n) => n * 2).toList();
list.any((n) => n > 3);
list.every((n) => n > 0);
list.sort();  list.reversed.toList();
list.fold<int>(0, (sum, n) => sum + n);
list.reduce((a, b) => a + b);
list.firstWhere((n) => n > 2);
list.indexWhere((n) => n == 3);
list.take(2).toList();  list.skip(1).toList();
list.expand((n) => [n, n * 10]).toList();

// Spread, collection if/for
[...list, 5, 6]                      // spread operator
[for (var i in list) i * 2]         // collection for
[if (flag) 99]                        // collection if
[...?nullableList]                     // null-aware spread

// Map
var map = <String, int>{'a': 1, 'b': 2};
map['c'] = 3;
map.containsKey('a');  map.containsValue(1);
map.forEach((k, v) => print('$k: $v'));
map.entries;  map.keys;  map.values;
map.update('a', (v) => v + 10);
map.removeWhere((k, v) => v > 2);
map.putIfAbsent('d', () => 4);
map.map((k, v) => MapEntry(k, v * 2));

// Set
var set = <int>{1, 2, 3};
set.add(4);  set.contains(1);
set.union({3,4,5});
set.intersection({2,3});
set.difference({1});

// Unmodifiable collections
final immutable = List.unmodifiable([1,2,3]);
const constList = [1, 2, 3];  // deeply immutable
🏛️

Classes & OOP

class Person {
  final String name;
  int _age;              // private (underscore = library-level)

  Person(this.name, this._age);         // shorthand constructor
  Person.guest() : name = 'Guest', _age = 0;  // named constructor

  int get age => _age;                   // getter
  set age(int val) => _age = val;        // setter

  @override
  String toString() => '$name ($_age)';
}

// Const constructor (compile-time objects)
class Point {
  final double x, y;
  const Point(this.x, this.y);
}
const origin = Point(0, 0);

// Factory constructor
class Logger {
  static final _cache = <String, Logger>{};
  factory Logger(String name) => _cache.putIfAbsent(name, () => Logger._internal(name));
  Logger._internal(this.name);
  final String name;
}

// Inheritance
class Student extends Person {
  String school;
  Student(super.name, super.age, this.school);

  @override
  String toString() => '${super.toString()} at $school';
}

// Abstract class + Interface
abstract class Shape {
  double area();           // abstract method
  void describe() => print('Shape with area ${area()}');
}

// Implements (every class is an implicit interface)
class MockPerson implements Person {
  @override String get name => 'Mock';
  // must implement ALL members
}

// Mixin
mixin Flyable {
  void fly() => print('Flying!');
}
mixin Swimmable {
  void swim() => print('Swimming!');
}
class Duck extends Animal with Flyable, Swimmable {}

// Mixin class (Dart 3+, can be both mixin and class)
mixin class Musician {
  void play() => print('Playing!');
}

// Sealed class (Dart 3+ — exhaustive pattern matching)
sealed class Result {}
class Success extends Result { final String data; Success(this.data); }
class Failure extends Result { final String error; Failure(this.error); }
class Loading extends Result {}

// Enum with members
enum Status {
  active('Active'), inactive('Inactive'), pending('Pending');
  final String label;
  const Status(this.label);
}

// Operator overloading
class Vector {
  final double x, y;
  const Vector(this.x, this.y);
  Vector operator +(Vector other) => Vector(x + other.x, y + other.y);
  bool operator ==(Object other) =>
      other is Vector && x == other.x && y == other.y;
  @override int get hashCode => Object.hash(x, y);
}
🔷

Generics & Patterns

// Generic class
class Box<T> {
  final T value;
  Box(this.value);
}
var box = Box<int>(42);

// Generic function
T first<T>(List<T> items) => items.first;

// Bounded generics
T max<T extends Comparable<T>>(T a, T b) => a.compareTo(b) >= 0 ? a : b;

// Covariant
class Animal {}
class Cat extends Animal {}
class Cage<T extends Animal> {
  void put(covariant T animal) {}
}

// ── Patterns (Dart 3+) ──

// Destructuring patterns
var (a, b) = (1, 2);                     // record destructuring
var [x, y, ...rest] = [1, 2, 3, 4];   // list pattern
var {'name': name, 'age': age} = json; // map pattern

// Pattern matching in switch
String describe(Result r) => switch (r) {
  Success(data: var d) => 'Got: $d',
  Failure(error: var e) => 'Error: $e',
  Loading() => 'Loading...',
};

// Guard clause
switch (value) {
  case int n when n > 0:
    print('positive: $n');
  case int n:
    print('non-positive: $n');
}

// if-case
if (json case {'name': String name, 'age': int age}) {
  print('$name is $age');
}

// Records (Dart 3+)
(int, int) swap((int, int) pair) => (pair.$2, pair.$1);
({String name, int age}) person = (name: 'Alice', age: 30);
print(person.name);
🌀

Async & Futures

// Future (async/await)
Future<String> fetchData() async {
  var response = await http.get(Uri.parse(url));
  return response.body;
}

// Error handling
try {
  var data = await fetchData();
} on SocketException {
  print('No internet');
} catch (e, stackTrace) {
  print('Error: $e');
  print('Stack: $stackTrace');
}

// Multiple futures
var results = await Future.wait([fetch1(), fetch2()]);
var first = await Future.any([fetch1(), fetch2()]);  // first to complete

// Future constructors
Future.delayed(Duration(seconds: 2), () => 'delayed');
Future.value(42);                              // already resolved
Future.error(Exception('oops'));               // already rejected

// Future chaining
fetchUser()
  .then((user) => fetchPosts(user.id))
  .then((posts) => print(posts))
  .catchError((e) => print('Error: $e'))
  .whenComplete(() => print('Done'));

// Streams
Stream<int> countStream(int max) async* {
  for (var i = 0; i < max; i++) {
    await Future.delayed(Duration(seconds: 1));
    yield i;
  }
}

await for (var val in countStream(5)) {
  print(val);
}

// Stream transformations
stream.map((n) => n * 2);
stream.where((n) => n > 5);
stream.expand((n) => [n, n * 10]);
stream.take(3);
stream.distinct();
stream.handleError((e) => print(e));
stream.listen(
  (data) => print(data),
  onError: (e) => print(e),
  onDone: () => print('done'),
  cancelOnError: false,
);

// StreamController (custom streams)
final controller = StreamController<int>.broadcast();
controller.sink.add(1);
controller.stream.listen((val) => print(val));
controller.close();

// Isolates (true parallelism)
var result = await Isolate.run(() => heavyComputation());
// or use compute() in Flutter
🛡️

Error Handling

// try / on / catch / finally
try {
  riskyOperation();
} on FormatException catch (e) {
  print('Format error: $e');
} on IOException {
  print('I/O error');            // catch without binding
} catch (e, stackTrace) {
  print('Unknown: $e');
  print(stackTrace);
} finally {
  cleanup();
}

// Throw
throw FormatException('Invalid input');
throw 'Something went wrong';        // can throw any object

// Custom exception
class ApiException implements Exception {
  final String message;
  final int statusCode;
  ApiException(this.message, this.statusCode);
  @override
  String toString() => 'ApiException($statusCode): $message';
}

// Rethrow
try {
  riskyOperation();
} catch (e) {
  log(e);
  rethrow;  // preserves original stack trace
}

// Result pattern (functional style)
sealed class Result<T> {}
class Ok<T> extends Result<T> { final T value; Ok(this.value); }
class Err<T> extends Result<T> { final String error; Err(this.error); }

Result<User> getUser(int id) {
  try {
    return Ok(fetchUser(id));
  } catch (e) {
    return Err(e.toString());
  }
}
🧩

Extensions & Utilities

// Extension methods
extension StringUtils on String {
  String get reversed => split('').reversed.join();
  bool get isEmail => RegExp(r'^[\w.-]+@[\w.-]+\.\w+$').hasMatch(this);
  String truncate(int len) => length > len ? '${substring(0, len)}...' : this;
}
'hello'.reversed;       // 'olleh'
'a@b.com'.isEmail;      // true

// Extension types (Dart 3.3+, zero-cost wrappers)
extension type UserId(int id) {
  bool get isValid => id > 0;
}
var uid = UserId(42);
uid.isValid;  // true

// RegExp
var re = RegExp(r'\d+');
re.hasMatch('abc123');          // true
re.firstMatch('abc123')?.group(0);  // '123'
'abc123def456'.replaceAll(re, '#');  // 'abc#def#'

// DateTime
var now = DateTime.now();
var date = DateTime(2026, 4, 1);
var parsed = DateTime.parse('2026-04-01');
now.difference(date).inDays;
now.add(Duration(hours: 3));
now.isBefore(date);  now.isAfter(date);

// JSON encoding/decoding
import 'dart:convert';
var jsonStr = jsonEncode({'name': 'Alice', 'age': 30});
var map = jsonDecode(jsonStr) as Map<String, dynamic>;

// File I/O
import 'dart:io';
var content = await File('data.txt').readAsString();
await File('output.txt').writeAsString('Hello');
var lines = await File('data.txt').readAsLines();
📱

Flutter Essentials

// ── Stateless Widget ──
class Greeting extends StatelessWidget {
  final String name;
  const Greeting({super.key, required this.name});

  @override
  Widget build(BuildContext context) {
    return Text('Hello $name');
  }
}

// ── Stateful Widget ──
class Counter extends StatefulWidget {
  const Counter({super.key});
  @override
  State<Counter> createState() => _CounterState();
}
class _CounterState extends State<Counter> {
  int _count = 0;
  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () => setState(() => _count++),
      child: Text('Count: $_count'),
    );
  }
}

// ── Common Layouts ──
Column(
  crossAxisAlignment: CrossAxisAlignment.start,
  children: [
    Text('Title', style: Theme.of(context).textTheme.headlineMedium),
    const SizedBox(height: 8),
    Row(children: [Icon(Icons.home), Text('Home')]),
    Expanded(child: ListView.builder(
      itemCount: items.length,
      itemBuilder: (ctx, i) => ListTile(title: Text(items[i])),
    )),
  ],
)

// ── Navigation ──
Navigator.push(context, MaterialPageRoute(builder: (_) => DetailPage()));
Navigator.pop(context);
Navigator.pushNamed(context, '/detail', arguments: {'id': 42});

// GoRouter (declarative)
final router = GoRouter(routes: [
  GoRoute(path: '/', builder: (_, __) => HomePage()),
  GoRoute(path: '/detail/:id', builder: (_, state) =>
      DetailPage(id: state.pathParameters['id']!)),
]);

// ── State Management (Riverpod) ──
final counterProvider = StateProvider<int>((_) => 0);

class MyWidget extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider);
    return ElevatedButton(
      onPressed: () => ref.read(counterProvider.notifier).state++,
      child: Text('$count'),
    );
  }
}

// AsyncNotifierProvider (Riverpod)
final usersProvider = AsyncNotifierProvider<UsersNotifier, List<User>>(
    UsersNotifier.new);

class UsersNotifier extends AsyncNotifier<List<User>> {
  @override
  Future<List<User>> build() async => await api.fetchUsers();
}

// ── HTTP & Dio ──
final dio = Dio(BaseOptions(baseUrl: 'https://api.example.com'));
final response = await dio.get('/users');
final users = (response.data as List).map((j) => User.fromJson(j)).toList();

// ── JSON Serialization (freezed + json_serializable) ──
@freezed
class User with _$User {
  const factory User({
    required int id,
    required String name,
    @Default('') String email,
  }) = _User;
  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
}

// ── Flutter CLI ──
// flutter create my_app
// flutter run -d chrome
// flutter build apk --release
// flutter build ios --release
// flutter pub add provider
// flutter pub run build_runner build
// dart run build_runner watch --delete-conflicting-outputs