Advanced Dart
Irbisa · cheatsheetSeptember 13, 2026

Advanced Dart

Generics, extensions, mixins, isolates, dart:core advanced

Middle Developer20 itemscompressed for a skim
  1. 01

    What are Isolates in Dart and when should you use them?

    Hard

    Dart is single-threaded, but Isolates provide TRUE parallelism.

    // ── Isolate.run() — simplest (Dart 2.19+) ──────────
    import 'dart:isolate';
    
    Future<List<int>> sortHeavy(List<int> data) async {
      return Isolate.run(() {
        final copy = List<int>.from(data);
    …
  2. 02

    How do generics and type bounds work in Dart?

    Medium

    Generics let a class or function declare placeholder types that the caller fills in.

    // Generic class with a bound
    class Cache<K, V extends Object> {
      final _store = <K, V>{};
      void put(K k, V v) => _store[k] = v;
      V? get(K k) => _store[k];
    }
    …
  3. 03

    What are extension methods, and how do they differ from an extension type?

    Medium

    Extensions bolt methods, getters, setters and operators onto a type you don't own, resolved statically at compile time.

    // Extension on a built-in type
    extension StringX on String {
      String toSlug() => toLowerCase()
          .replaceAll(RegExp(r'[^a-z0-9]+'), '-')
          .replaceAll(RegExp(r'^-+|-+$'), '');
      bool get isBlank => trim().isEmpty;
    …
  4. 04

    What are mixins and how do they differ from inheritance and interfaces?

    Medium

    A mixin is a reusable bundle of methods you fold into a class without single-inheritance constraints.

    // Standalone mixin
    mixin Tappable {
      int _taps = 0;
      int get taps => _taps;
      void tap() => _taps++;
    }
    …
  5. 05

    What are async* and sync* generators and when should you use them?

    Medium

    Generators produce sequences lazily.

    // sync* — recursive walk over a tree
    Iterable<File> walk(Directory dir) sync* {
      for (final entity in dir.listSync()) {
        if (entity is File) yield entity;
        if (entity is Directory) yield* walk(entity); // delegate
      }
    …
  6. 06

    What can an enhanced enum do that a plain enum cannot?

    Easy

    An enhanced enum can declare fields, a const constructor, getters, methods, and implement interfaces.

    enum HttpMethod {
      get('GET', false),
      post('POST', true),
      put('PUT', true),
      delete('DELETE', false);
    …
  7. 07

    What are records and patterns in Dart 3, and which older idioms do they replace?

    Medium

    Records are anonymous, immutable, structurally-typed tuples, and patterns destructure values in place.

    // Multi-value return without a class
    (int code, String body) callApi() => (200, '{"ok":true}');
    
    void main() {
      final (code, body) = callApi();    // destructure
      print('$code $body');
    …
  8. 08

    What are sealed classes and how do they enable exhaustive switches?

    Hard

    A sealed class is one whose complete set of subtypes the compiler knows, because every subtype must live in the same library.

    // Discriminated state, exhaustively checked
    sealed class AuthState {}
    class Anonymous extends AuthState {}
    class Authenticating extends AuthState { final String email; Authenticating(this.email); }
    class Authenticated extends AuthState { final User user; Authenticated(this.user); }
    class AuthFailed extends AuthState { final Object error; AuthFailed(this.error); }
    …
  9. 09

    What is dart:ffi and when should you reach for it instead of platform channels?

    Hard

    dart:ffi lets Dart call C-ABI functions directly — no MethodChannel, no platform-thread hop, no message serialization.

    import 'dart:ffi';
    import 'package:ffi/ffi.dart';
    
    typedef _HashC    = Int32 Function(Pointer<Uint8> data, Int32 len);
    typedef _HashDart = int Function(Pointer<Uint8> data, int len);
    …
  10. 10

    Why does list.map(...) run nothing until you call toList(), and when does that laziness bite?

    Medium

    map, where, expand, take and skip all return a lazy view, so the callback does not run until something actually iterates the result.

    final nums = [1, 2, 3, 4, 5];
    
    final doubled = nums.map((n) {
      print('mapping $n');       // does NOT run here
      return n * 2;
    });
    …
  11. 11

    Why does Dart refuse to promote a nullable field after a null check, and what do you do about it?

    Medium

    Type promotion only applies where the compiler can prove the value cannot change between the check and the use.

    class Profile {
      String? nickname;        // public and mutable — never promoted
      final String? _email;    // private and final — promoted in this library
    
      Profile(this._email);
    …
  12. 12

    What does the late modifier actually do, and when is it the wrong tool?

    Easy

    late lets a non-nullable variable be initialised after its declaration, moving the null check from compile time to run time.

    class _EditorState extends State<Editor> with SingleTickerProviderStateMixin {
      // Lazy: the initialiser needs `this`, which is not ready at construction
      late final AnimationController _anim = AnimationController(
        vsync: this,
        duration: const Duration(milliseconds: 200),
      );
    …
  13. 13

    A crash report says "Bad state: No element" and points at a line calling firstWhere — what happened and what do you write instead?

    Easy

    firstWhere has no null to fall back on, so when nothing matches it throws a StateError.

    import 'package:collection/collection.dart';
    
    void demo(List<Order> orders, String id) {
      // ❌ throws "Bad state: No element" the first time nothing matches
      final pending = orders.firstWhere((o) => o.status == 'pending');
    …
  14. 14

    You override == on a value class, drop two equal instances into a Set, and both survive — what did you forget?

    Easy

    hashCode. A hash set picks the bucket by hashCode first, so two objects with different hashes never get compared with == at all.

    import 'package:flutter/foundation.dart';
    
    class Money {
      final int cents;
      final String currency;
      const Money(this.cents, this.currency);
    …
  15. 15

    Why does jsonDecode(body) as List<Map<String, dynamic>> throw a TypeError even though the JSON really is an array of objects?

    Medium

    Dart generics are reified, so the decoded value really is a List<dynamic> at run time, and List<dynamic> is not a subtype of List<Map<String, dynamic>>.

    import 'dart:convert';
    
    void parse(String body) {
      final decoded = jsonDecode(body);      // static type dynamic
      print(decoded.runtimeType);            // List<dynamic>
    …
  16. 16

    An extension method compiles on a Dog variable, runs the wrong body through an Animal one, and throws on a dynamic — what single rule explains all three?

    Medium

    Extension members are resolved at compile time from the static type of the receiver — there is no dynamic dispatch and no entry in the object's interface.

    class Animal {}
    class Dog extends Animal {}
    
    extension AnimalX on Animal {
      String speak() => 'animal';
    }
    …
  17. 17

    removeListener(() => _refresh()) leaves the listener attached and the screen keeps rebuilding after dispose — what is going on?

    Medium

    Every evaluation of a closure expression creates a new function object, so you are asking the notifier to remove a function it has never been given.

    class _FeedState extends State<Feed> {
      @override
      void initState() {
        super.initState();
        // ❌ a brand-new function object; nothing can ever match it again
        widget.model.addListener(() => _refresh());
    …
  18. 18

    In class Repo extends Base with Logging, Caching, which override runs first and what does super mean inside Logging?

    Medium

    The mixins are applied left to right on top of the superclass, so the chain is Base → Logging → Caching → Repo, and super inside Logging means Base.

    class Base {
      String describe() => 'Base';
    }
    
    mixin Logging on Base {          // `on` gives super a known type
      @override
    …
  19. 19

    You moved JSON parsing into compute() and the frame drop got worse instead of better — what would you measure and change?

    Hard

    Measure the size of what crosses the boundary: both directions of the hop deep-copy the message, and that copy runs on the sending isolate, so a big payload costs you the very frame you were trying to save.

    import 'dart:convert';
    import 'dart:isolate';
    import 'package:flutter/foundation.dart';
    import 'package:flutter/services.dart';
    
    // ❌ decode on the UI isolate, then copy the whole tree across and back
    …
  20. 20

    A cache keyed by object holds decoded images alive forever — which dart:core tools let the GC reclaim them, and why is Finalizer not a destructor?

    Hard

    WeakReference and Expando let you point at an object without keeping it alive; Finalizer only tells you afterwards that one was collected, and it is explicitly allowed never to run.

    class DecodedImage {
      final int bytes;
      DecodedImage(this.bytes);
    }
    
    // ✅ a cache the collector is allowed to win against
    …