OOP in Dart
Irbisa · cheatsheetSeptember 13, 2026

OOP in Dart

Classes, inheritance, interfaces, mixins, and abstract classes

Junior Developer20 itemscompressed for a skim
  1. 01

    Explain classes, constructors, and named constructors in Dart.

    Easy

    Dart classes can declare several kinds of constructor: a default one, named ones, factory constructors and const constructors.

    class User {
      final String name;
      final String email;
      int age;
    
      // Default constructor with initializer shorthand
    …
  2. 02

    What is the difference between abstract class, interface, and mixin in Dart?

    Medium

    The keyword decides what you get: extends inherits an implementation, implements takes only the contract, and with composes a mixin.

    // Abstract class
    abstract class Shape {
      double area();  // abstract — subclasses must implement
      String describe() => 'Area: ${area().toStringAsFixed(2)}'; // concrete
    }
    …
  3. 03

    What are extensions in Dart and when would you use them?

    Medium

    Extensions add methods, getters, setters and operators to a type you do not own, without subclassing or modifying it.

    // String extensions
    extension StringX on String {
      String get capitalized =>
          isEmpty ? '' : '${this[0].toUpperCase()}${substring(1)}';
    
      bool get isEmail =>
    …
  4. 04

    How does operator overloading work in Dart?

    Medium

    Dart lets you overload a fixed list of operators by declaring them as methods with the operator keyword.

    import 'dart:math';
    
    class Vector {
      final double x, y;
      const Vector(this.x, this.y);
    …
  5. 05

    What is the difference between covariant and contravariant types in Dart?

    Hard

    Dart generics are invariant, so List<Dog> is not a subtype of List<Animal> even though Dog extends Animal.

    class Animal {
      void eat() {}
    }
    class Dog extends Animal {
      void bark() {}
    }
    …
  6. 06

    What are factory constructors and when would you use them?

    Medium

    A factory constructor may return an existing instance, a cached one, or an instance of a subtype instead of always building a new object.

    // ── Singleton pattern ──────────────────────
    class Database {
      static final Database _instance = Database._internal();
    
      factory Database() {
        return _instance;
    …
  7. 07

    Explain mixins in Dart and when to use them.

    Medium

    Mixins let you reuse code across unrelated class hierarchies — you write the behaviour once and apply it with with.

    // ── Basic mixin ────────────────────────────
    mixin Swimmer {
      void swim() => print('Swimming...');
    }
    
    mixin Flyer {
    …
  8. 08

    What is the difference between extends, implements, and with in Dart?

    Medium

    Each keyword hands you a different thing: extends the parent's implementation, implements only its contract, with a mixin's behaviour.

    // ── extends — Inheritance ──────────────────
    class Animal {
      String name;
      Animal(this.name);
    
      void makeSound() => print('Some sound');
    …
  9. 09

    Dart has no private keyword, so how do you hide a member — and what exactly is it hidden from?

    Easy

    An identifier that starts with an underscore is private to its library, not to its class.

    class Cart {
      final List<Item> _items = [];      // private to this library
      double _total = 0;
    
      List<Item> get items => List.unmodifiable(_items); // read-only view
      double get total => _total;
    …
  10. 10

    When should a member be static, and what can static members not do?

    Easy

    A static member belongs to the class itself, so it has no this and no subclass can override it.

    class ApiConfig {
      static const baseUrl = 'https://api.example.com';  // compile-time constant
      static final _log = Logger('api');    // lazy — built on first read
      static int requestCount = 0;          // shared mutable state (be careful)
    
      static Uri endpoint(String path) => Uri.parse('$baseUrl/$path');
    …
  11. 11

    What do the base, interface and final class modifiers control in Dart 3?

    Hard

    Each modifier takes away a capability that an ordinary class hands to every other library.

    // A contract others implement, whose body you keep free to change
    abstract interface class UserRepository {
      Future<User> byId(String id);
    }
    
    class ApiUserRepository implements UserRepository {   // ✅ implements
    …
  12. 12

    Why are Flutter model classes usually immutable, and what does a copyWith method buy you?

    Medium

    Flutter decides what to rebuild by comparing the previous object with the next one, and an object mutated in place always looks unchanged.

    @immutable
    class CartState {
      final List<Item> items;
      final bool isLoading;
      final String? error;
    …
  13. 13

    A cart recomputes its total in three places — should that be a stored field or a getter, and does turning a public field into a getter break callers?

    Easy

    A getter is a method that is read like a field, so derived values belong in a getter and only genuinely stored state belongs in a field.

    class Cart {
      final List<Item> items;
      Cart(this.items);
    
      // derived: cannot drift out of sync with items
      double get subtotal => items.fold(0.0, (sum, i) => sum + i.price);
    …
  14. 14

    Widget constructors used to end with : super(key: key) and now just say super.key — what does that syntax do, and when can you not use it?

    Easy

    super.key is a super parameter: you declare the parameter in your constructor and it is forwarded straight to the superclass constructor, so the initializer list disappears and the type is inherited from the parameter it forwards to.

    // Before Dart 2.17
    class OldCard extends StatelessWidget {
      final String title;
      const OldCard({Key? key, required this.title}) : super(key: key);
    
      @override
    …
  15. 15

    You put const in front of a subtree and the profiler shows it no longer rebuilds — what does const do to the object, and what does the element tree do with that?

    Medium

    A const constructor builds the object at compile time and canonicalizes it, so every const Text('Hi') with the same arguments anywhere in the program is literally the same instance — and Flutter keeps the existing element when the new widget is that same object.

    class PriceTag extends StatelessWidget {
      final String label; // final: required for a const constructor
      const PriceTag(this.label, {super.key});
    
      @override
      Widget build(BuildContext context) => Text(label);
    …
  16. 16

    Your enum OrderStatus is followed by three switch statements mapping it to a label, an API string and a retry policy — what replaces them?

    Medium

    An enhanced enum: since Dart 2.17 an enum can declare final fields, a const constructor, getters, methods and implements/with clauses, so data that is true about a value travels with the value instead of living in switches somewhere else.

    enum OrderStatus implements Comparable<OrderStatus> {
      pending('pending', 'Waiting for payment', retryable: true),
      paid('paid', 'Paid', retryable: false),
      cancelled('canceled', 'Cancelled', retryable: false); // API spells it with one l
    
      const OrderStatus(this.apiValue, this.label, {required this.retryable});
    …
  17. 17

    You added operator == to a model and a Set of them still holds duplicates — what is missing, and what happens if you mutate an object already in the set?

    Medium

    hashCode is missing: Set and Map bucket by hash first and only call == on objects that land in the same bucket, so two equal objects with different hash codes never get compared at all.

    class Tag {
      final String name;
      final int weight;
      const Tag(this.name, this.weight);
    
      // ❌ == alone: the Set buckets by the identity hash, so duplicates survive
    …
  18. 18

    Your BaseScreen has grown to 400 lines, six subclasses override half of it, and one change to the base broke three of them — what is the refactor?

    Medium

    Stop inheriting and start composing: the subclasses should hold the pieces they need instead of overriding hooks the base decides when to call.

    // ❌ template-method base: subclasses depend on when the base calls what
    abstract class BaseScreen extends StatefulWidget {
      const BaseScreen({super.key});
      bool get showAppBar => true;      // flags that switch behaviour
      bool get trackAnalytics => true;
      String get screenName;
    …
  19. 19

    In class Repo extends Base with Caching, Logging, both mixins override fetch and call super.fetch() — whose body runs first, and why does swapping them change the result?

    Hard

    Logging.fetch runs first, because with is applied left to right and the rightmost mixin ends up closest to the class.

    abstract class Base {
      Future<String> fetch(String id) async => 'from network: $id';
    }
    
    mixin Caching on Base {
      final _cache = <String, String>{};
    …
  20. 20

    A base constructor calls an overridable onInit(), and inside it the subclass's late final field throws LateInitializationError — what is Dart's construction order?

    Hard

    The subclass is only half built when the base body runs: Dart evaluates the derived class's field initializers and initializer list, then the entire superclass constructor, and only then the derived constructor's body.

    abstract class Repo {
      Repo() {
        onInit(); // ❌ virtual call from a constructor body
      }
      void onInit();
    }
    …