← All notes

Dart Basics Cheatsheet

Cheatsheet · 28 Sep 2026

AI-generated Written with AI, solely for personal reference.

Dart is the language behind Flutter: typed, null-safe and compiled. Snippets without a main go inside void main() { ... }.

Run Dart

dart --version
dart run hello.dart          # run a file
dart create my_app           # new console project
dart format .                # format code
dart analyze                 # check for problems
void main() {
  print('Hello, world!');
}

Variables

var name = 'Maxine';         // type inferred (String)
int age = 30;                // explicit type
double height = 1.75;
bool isActive = true;
final city = 'Springfield';          // set once, at runtime
const pi = 3.14159;          // fixed at compile time

age += 1;
print('$name is $age');      // Maxine is 31
print('Next year: ${age + 1}');  // Next year: 32
print(height.runtimeType);   // double
print('$isActive $city $pi');
Keyword Can change? When is it set?
var / a type Yes Any time
final No Once, at runtime
const No At compile time
late Set once, later Before first use

Null safety

Types can’t hold null unless you add ?.

String? findNickname(String name) => name == 'Maxine' ? 'Max' : null;

void main() {
  String? nickname = findNickname('Ali');  // String? : may be null (it is here)
  print(nickname ?? 'no nickname');        // ?? : fallback if null
  print(nickname?.length);                 // ?. : null instead of an error

  nickname ??= 'Max';                      // assign only if null
  print(nickname.length);                  // 3: Dart knows it's not null now

  String sure = findNickname('Maxine')!;   // ! : "I promise it's not null" (throws if wrong)
  print(sure);                             // Max
}

Strings and numbers

var s = 'hello world';
print(s.toUpperCase());                  // HELLO WORLD
print(s.split(' '));                     // [hello, world]
print(s.contains('world'));              // true
print(s.substring(0, 5));                // hello
print('ab' * 3);                         // ababab

var multi = '''
Line one
Line two''';
print(multi);

print(7 ~/ 2);                           // 3   integer division
print(7 / 2);                            // 3.5
print(7 % 2);                            // 1
print(int.parse('42') + 1);              // 43
print(3.14159.toStringAsFixed(2));       // 3.14
print(42.toString().length);             // 2

Collections

// List: ordered
var fruits = ['apple', 'banana'];
fruits.add('cherry');
fruits.remove('banana');
print(fruits);                           // [apple, cherry]
print(fruits.length);                    // 2
print(fruits.first);                     // apple

// Set: unique items
var tags = {'dart', 'flutter'};
tags.add('dart');                        // already there, so nothing changes
print(tags);                             // {dart, flutter}

// Map: key -> value
var ages = {'Ali': 30, 'Siti': 25};
ages['Aisha'] = 28;
print(ages['Ali']);                      // 30
print(ages.containsKey('Bob'));          // false
ages.forEach((name, age) => print('$name: $age'));

// Spread, collection if and for
var more = [...fruits, 'date'];
var showExtra = true;
var menu = ['home', if (showExtra) 'extra', for (var f in fruits) f.toUpperCase()];
print(more);                             // [apple, cherry, date]
print(menu);                             // [home, extra, APPLE, CHERRY]

// Handy methods
var nums = [3, 1, 4, 1, 5];
print(nums.map((n) => n * 2).toList());  // [6, 2, 8, 2, 10]
print(nums.where((n) => n > 2).toList()); // [3, 4, 5]
print(nums.reduce((a, b) => a + b));     // 14

Control flow

var score = 72;
if (score >= 80) {
  print('A');
} else if (score >= 60) {
  print('B');
} else {
  print('C');
}

var label = score >= 60 ? 'pass' : 'fail';
print(label);                            // pass

for (var i = 0; i < 3; i++) {
  print(i);                              // 0 1 2
}
for (var fruit in ['apple', 'kiwi']) {
  print(fruit);
}
var n = 3;
while (n > 0) {
  n--;
}

// switch expression (Dart 3)
var day = 2;
var name = switch (day) {
  1 => 'Mon',
  2 => 'Tue',
  _ => 'Other',
};
print(name);                             // Tue

Functions

int add(int a, int b) => a + b;          // => for one-expression bodies

// Named parameters in {}; required, or with a default
String greet({required String name, String greeting = 'Hello'}) {
  return '$greeting, $name!';
}

// Optional positional parameters in []
String describe(String item, [String? colour]) =>
    colour == null ? item : '$colour $item';

void main() {
  print(add(2, 3));                              // 5
  print(greet(name: 'Maxine'));                  // Hello, Maxine!
  print(greet(name: 'Maxine', greeting: 'Hi'));  // Hi, Maxine!
  print(describe('car'));                        // car
  print(describe('car', 'red'));                 // red car

  // Functions are values
  var square = (int x) => x * x;
  print([1, 2, 3].map(square).toList());         // [1, 4, 9]
}

Classes

class BankAccount {
  final String owner;
  double _balance;                     // _ makes it private to the file

  BankAccount(this.owner, [this._balance = 0]);   // this.x assigns fields

  BankAccount.empty(String owner) : this(owner);  // named constructor

  double get balance => _balance;      // getter

  void deposit(double amount) {
    if (amount <= 0) throw ArgumentError('amount must be positive');
    _balance += amount;
  }

  @override
  String toString() => 'BankAccount($owner, $_balance)';
}

void main() {
  var account = BankAccount('Maxine', 100);
  account.deposit(50);
  print(account.balance);              // 150.0
  print(account);                      // BankAccount(Maxine, 150.0)
  print(BankAccount.empty('Siti'));    // BankAccount(Siti, 0.0)
}

Inheritance, interfaces and mixins

abstract class Animal {                // can't be created directly
  final String name;
  Animal(this.name);
  String speak();                      // subclasses must implement
  String introduce() => '$name says ${speak()}';
}

class Dog extends Animal {
  Dog(super.name);                     // pass name to the parent
  @override
  String speak() => 'Woof';
}

mixin Swimmer {                        // reusable behaviour
  String swim() => 'swimming';
}

class Duck extends Animal with Swimmer {
  Duck(super.name);
  @override
  String speak() => 'Quack';
}

abstract interface class Printable {   // a contract, no code
  String printLabel();
}

class Invoice implements Printable {
  @override
  String printLabel() => 'Invoice #1';
}

void main() {
  print(Dog('Rex').introduce());       // Rex says Woof
  var duck = Duck('Don');
  print('${duck.introduce()}, ${duck.swim()}');  // Don says Quack, swimming
  print(Invoice().printLabel());       // Invoice #1
}

Enums, records and patterns

enum Status {
  active('Active'),
  paused('Paused');

  final String label;
  const Status(this.label);
}

// A record bundles several values without a class
(String, int) nameAndAge() => ('Maxine', 30);

void main() {
  var s = Status.paused;
  print(s.label);                          // Paused
  print(Status.values.length);             // 2

  var (name, age) = nameAndAge();          // destructure a record
  print('$name $age');                     // Maxine 30

  var point = (x: 3, y: 4);                // named record fields
  print(point.x + point.y);                // 7

  Object value = [1, 2];
  var text = switch (value) {
    int n when n > 10 => 'big number',
    int _ => 'number',
    [var a, var b] => 'pair of $a and $b',
    _ => 'something else',
  };
  print(text);                             // pair of 1 and 2
}

Errors

void main() {
  try {
    int.parse('abc');
  } on FormatException catch (e) {
    print('Bad number: ${e.message}');     // Bad number: Invalid radix-10 number
  } finally {
    print('done');
  }
}

throw raises any object, usually an Exception or Error subclass such as ArgumentError or StateError.

Async code

A Future is a value that arrives later; await waits for it inside an async function.

Future<String> fetchUser() async {
  await Future.delayed(Duration(milliseconds: 100));  // pretend network call
  return 'Maxine';
}

Stream<int> countdown(int from) async* {
  for (var i = from; i > 0; i--) {
    yield i;                                // emit values one at a time
  }
}

Future<void> main() async {
  print('loading...');
  var user = await fetchUser();
  print('Hello, $user');                    // Hello, Maxine

  var results = await Future.wait([fetchUser(), fetchUser()]);  // run in parallel
  print(results);                           // [Maxine, Maxine]

  await for (var n in countdown(3)) {
    print(n);                               // 3 2 1
  }
}