Dart Cheat Sheet β€” Null Safety, Async, Futures, Flutter | Dataplexa

Dart

null safety  Β·  async  Β·  futures  Β·  streams  Β·  classes  Β·  mixins  Β·  collections  Β·  Flutter

Sheet 2 of 3 Dart 3.x Beginner Printable

Variables & Null Safety

var Β· final Β· const Β· ? Β· ! Β· ??
Variable Declaration
// Type inferred
var name = 'Alice';
var age  = 25;

// Explicit type
String city   = 'Hyderabad';
int    score  = 92;
double pi     = 3.14159;
bool   active = true;

// final β€” set once at runtime
final url = 'https://api.example.com';

// const β€” compile-time constant
const MAX = 100;

// dynamic β€” opt out of type checking
dynamic anything = 'hello';
anything = 42;  // OK
Null Safety β€” ?, !, ??
// Non-nullable by default
String  a = 'hi';   // never null
String? b = null;  // nullable

// ? β€” conditional member access
b?.toUpperCase()  // null if b is null
b?.length        // null if b is null

// ! β€” null assertion (throws if null)
String c = b!;   // trust me, not null

// ?? β€” if-null operator
String d = b ?? 'default';

// ??= β€” assign if null
b ??= 'assigned';

// late β€” initialise later (non-null)
late String username;
username = 'alice';  // before use
Type System & Conversions
// Check type
age is int           // true
age is String        // false
age is! String       // true

// Cast
dynamic v = 42;
int n = v as int;

// Parse strings
int    i = int.parse('42');
double d = double.parse('3.14');
int?   x = int.tryParse('bad');
// x == null (doesn't throw)

// Convert to string
42.toString()         // '42'
3.14.toStringAsFixed(1) // '3.1'
Sound null safety is Dart's biggest feature since 2.12. The compiler guarantees that non-nullable variables cannot be null at runtime β€” no more null pointer exceptions if you follow the type system.

Collections

List Β· Map Β· Set Β· spread Β· if Β· for
List
// Typed list
List<int> nums = [1, 2, 3];
var fruits = ['apple', 'mango'];

fruits.add('cherry');
fruits.remove('apple');
fruits.length           // 2
fruits.first            // 'mango'
fruits.last             // 'cherry'
fruits.contains('mango')  // true
fruits.reversed.toList()

// Spread operator
var all = [...fruits, 'kiwi'];

// Collection if / for
var menu = [
  'Home',
  if (isAdmin) 'Admin',
  for (var p in pages) p.title,
];
Map & Set
// Map β€” key:value
var user = {
  'name': 'Alice',
  'age':  25,
};
user['email'] = 'a@b.com';
user.containsKey('age')  // true
user.remove('age')
user.keys   // Iterable of keys
user.values // Iterable of values
user.entries // Iterable of MapEntry

// Set β€” unique values
var tags = {'dart', 'flutter', 'dart'};
// {'dart', 'flutter'} β€” deduped
tags.add('mobile');
tags.contains('flutter')  // true
tags.union({'web'})
tags.intersection(other)

Functions

arrow Β· named params Β· optional Β· closures
Define & Call
// Regular function
int add(int a, int b) {
  return a + b;
}

// Arrow β€” single expression
int square(int x) => x * x;

// Named parameters (with {})
void greet({required String name,
             String title = 'Mr'}) {
  print('Hello $title $name');
}
greet(name: 'Alice');
greet(name: 'Bob', title: 'Dr');

// Optional positional (with [])
String tag(String s, [String t = 'div'])
    => '<$t>$s</$t>';

// First-class / lambda
var double = (int x) => x * 2;
[1,2,3].map(double).toList()
// [2, 4, 6]
String Interpolation & Cascade
var name = 'Alice';
var age  = 25;

// $ for variables
print('Hello $name');

// ${} for expressions
print('${name.toUpperCase()} is ${age+1}');

// Multi-line strings
var text = '''
  Hello
  World
''';

// Cascade .. β€” chain calls
var sb = StringBuffer()
  ..write('Hello')
  ..write(' World');
print(sb.toString());

Classes, Inheritance & Mixins

class Β· extends Β· implements Β· mixin Β· with
Class Definition
class Animal {
  String name;
  int    age;

  // Constructor shorthand
  Animal(this.name, this.age);

  // Named constructor
  Animal.unknown()
      : name = 'Unknown',
        age  = 0;

  // Getter
  String get info =>
      '$name ($age)';

  // Setter
  set newName(String n) {
    name = n.trim();
  }

  void speak() => print('...');

  @override
  String toString() => info;
}
Inheritance & Interfaces
// extends β€” inherit one class
class Dog extends Animal {
  String breed;

  Dog(String name, this.breed)
      : super(name, 0);

  @override
  void speak() => print('Woof!');
}

// abstract class
abstract class Shape {
  double area();
  void describe() =>
      print('area=${area()}');
}

// implements β€” use as interface
class Circle implements Shape {
  final double r;
  Circle(this.r);
  @override
  double area() => 3.14159 * r * r;
}
Mixins
// mixin β€” reusable behaviour
mixin Flyable {
  void fly() => print('Flying!');
  int speed = 100;
}

mixin Swimmable {
  void swim() => print('Swimming!');
}

// with β€” apply mixins
class Duck extends Animal
    with Flyable, Swimmable {
  Duck() : super('Duck', 1);
}

var d = Duck();
d.fly();    // Flying!
d.swim();   // Swimming!

// on β€” restrict mixin to type
mixin Trainable on Animal {
  void train() =>
      print('$name trained');
}
Dart's this.field shorthand in constructors β€” Animal(this.name, this.age) β€” automatically assigns parameters to fields. No boilerplate needed.

Async / Await & Futures

Future Β· async Β· await Β· then Β· catchError
Future & async/await
// Future β€” value arriving later
Future<String> fetchUser(int id) async {
  await Future.delayed(
      Duration(seconds: 1));
  return 'User $id';
}

void main() async {
  String user = await fetchUser(1);
  print(user);  // 'User 1'
}

Future.value(42)
Future.error('oops')

// Run multiple in parallel
var results = await Future.wait([
  fetchUser(1),
  fetchUser(2),
]);
Error Handling in Async
Future<void> loadData() async {
  try {
    var data = await fetchUser(1);
    print(data);
  } catch (e) {
    print('Error: $e');
  } finally {
    print('Done');
  }
}

// .then() chain
fetchUser(1)
  .then((u) => print(u))
  .catchError((e) => print(e))
  .whenComplete(() => print('done'));

// FutureOr β€” sync or async
FutureOr<String> maybeAsync()
    => 'immediate';
Streams
// async* + yield β†’ Stream
Stream<int> count(int n) async* {
  for (var i=1; i<=n; i++) {
    await Future.delayed(
        Duration(seconds: 1));
    yield i;
  }
}

// await for β€” consume stream
await for (var n in count(3))
  print(n);  // 1, 2, 3

// Stream from Iterable
Stream.fromIterable([1,2,3])
  .map((x) => x * 2)
  .where((x) => x > 2)
  .listen((x) => print(x));

// StreamController
var ctrl = StreamController<int>();
ctrl.sink.add(1);
ctrl.stream.listen((v) => print(v));
ctrl.close();
async* + yield creates a generator function that produces a Stream. Use sync* + yield for synchronous Iterable generators.

Control Flow

if Β· switch Β· for Β· while Β· pattern matching
if / switch / loops
if (score >= 90) {
  print('A');
} else if (score >= 80) {
  print('B');
}

// switch (Dart 3 β€” exhaustive)
switch (day) {
  case 'Mon' || 'Fri':
    print('Busy');
  case 'Sat' || 'Sun':
    print('Weekend');
  default:
    print('Normal');
}

for (var fruit in fruits)
  print(fruit);

fruits.forEach((f) => print(f));

while (n > 0) { n--; }
do { n++; } while (n < 10);
Pattern Matching (Dart 3)
// switch expression
var grade = switch (score) {
  >= 90 => 'A',
  >= 80 => 'B',
  >= 70 => 'C',
  _     => 'F',
};

// Destructure records (Dart 3)
var point = (10, 20);
var (x, y) = point;  // x=10, y=20

// Pattern in if
if (obj case String s) {
  print('String: $s');
}

Enums & Records

enum Β· enhanced enum Β· record Β· typedef
Enhanced Enums (Dart 2.17+)
// Simple enum
enum Direction { north, south, east, west }

var d = Direction.north;
d.name   // 'north'
d.index  // 0

// Enhanced enum β€” with members
enum Status {
  active('Active', 200),
  inactive('Inactive', 404);

  final String label;
  final int    code;
  const Status(this.label, this.code);
}

Status.active.label  // 'Active'
Status.active.code   // 200
Records (Dart 3)
// Record β€” anonymous immutable struct
var point = (x: 10, y: 20);
point.x  // 10
point.y  // 20

// Return multiple values
(String, int) getUser() =>
    ('Alice', 25);

var (name, age) = getUser();

// typedef β€” name a type
typedef Point = ({int x, int y});
Point p = (x: 3, y: 4);

Flutter Essentials

Widget Β· StatefulWidget Β· setState Β· BuildContext Β· FutureBuilder
StatelessWidget & StatefulWidget
import 'package:flutter/material.dart';

// Stateless β€” no mutable state
class HelloWidget extends StatelessWidget {
  final String name;
  const HelloWidget({super.key,
                     required this.name});
  @override
  Widget build(BuildContext ctx)
      => Text('Hello $name');
}

// Stateful β€” mutable state
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 ctx) =>
      ElevatedButton(
        onPressed: () =>
            setState(() => _count++),
        child: Text('$_count'),
      );
}
Common Widgets
Scaffold(
  appBar: AppBar(title: Text('App')),
  body: Column(
    children: [
      Text('Hello'),
      SizedBox(height: 16),
      ElevatedButton(
        onPressed: (){},
        child: Text('Click'),
      ),
    ],
  ),
)

Row(children:[...])
Stack(children:[...])

ListView.builder(
  itemCount: items.length,
  itemBuilder: (ctx, i) =>
      Text(items[i]),
)

Container(
  width: 100, height: 100,
  color: Colors.blue,
  padding: EdgeInsets.all(8),
)
FutureBuilder & StreamBuilder
FutureBuilder<String>(
  future: fetchUser(1),
  builder: (ctx, snap) {
    if (snap.hasError)
      return Text('Error');
    if (!snap.hasData)
      return CircularProgressIndicator();
    return Text(snap.data!);
  },
)

StreamBuilder<int>(
  stream: count(5),
  builder: (ctx, snap) {
    if (!snap.hasData)
      return CircularProgressIndicator();
    return Text('${snap.data}');
  },
)
Always use const constructors for widgets that don't change β€” const Text('hi'). Flutter reuses them across rebuilds, dramatically improving performance.

Dart Mastery Checklist

sheet 2 complete
Null Safety & TypesKey point
Nullable type String? name
Safe access obj?.field
Fallback value val ?? 'default'
Assert non-null val!
Late initialise late String x;
Type check obj is String
Async & StreamsKey point
Async function Future<T> fn() async
Await a future var x = await fn();
Parallel futures Future.wait([...])
Create a stream async* { yield x; }
Consume a stream await for (var x in s)
Error handling try/catch with await
Classes & FlutterKey point
Constructor shorthandCls(this.field)
Inherit behaviour mixin + with
Stateless widget extends StatelessWidget
Trigger rebuild setState(() {...})
Async in UI FutureBuilder
Live data in UI StreamBuilder
Next up β†’ Sheet 3: Shell Scripting  Β·  bash Β· variables Β· loops Β· pipes Β· cron Β· grep Β· sed Β· awk