OOP in Dart
Classes, inheritance, interfaces, mixins, and abstract classes
- 01
Explain classes, constructors, and named constructors in Dart.
EasyDart 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 … - 02
What is the difference between abstract class, interface, and mixin in Dart?
MediumThe 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 } … - 03
What are extensions in Dart and when would you use them?
MediumExtensions 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 => … - 04
How does operator overloading work in Dart?
MediumDart lets you overload a fixed list of operators by declaring them as methods with the
operatorkeyword.import 'dart:math'; class Vector { final double x, y; const Vector(this.x, this.y); … - 05
What is the difference between covariant and contravariant types in Dart?
HardDart 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() {} } … - 06
What are factory constructors and when would you use them?
MediumA 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; … - 07
Explain mixins in Dart and when to use them.
MediumMixins 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 { … - 08
What is the difference between extends, implements, and with in Dart?
MediumEach 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'); … - 09
Dart has no
privatekeyword, so how do you hide a member — and what exactly is it hidden from?EasyAn 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
When should a member be
static, and what can static members not do?EasyA static member belongs to the class itself, so it has no
thisand 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
What do the
base,interfaceandfinalclass modifiers control in Dart 3?HardEach 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
Why are Flutter model classes usually immutable, and what does a copyWith method buy you?
MediumFlutter 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
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?
EasyA 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
Widget constructors used to end with
: super(key: key)and now just saysuper.key— what does that syntax do, and when can you not use it?Easysuper.keyis 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
You put
constin front of a subtree and the profiler shows it no longer rebuilds — what doesconstdo to the object, and what does the element tree do with that?MediumA
constconstructor builds the object at compile time and canonicalizes it, so everyconst 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
Your
enum OrderStatusis followed by three switch statements mapping it to a label, an API string and a retry policy — what replaces them?MediumAn enhanced enum: since Dart 2.17 an enum can declare final fields, a const constructor, getters, methods and
implements/withclauses, 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
You added
operator ==to a model and aSetof them still holds duplicates — what is missing, and what happens if you mutate an object already in the set?MediumhashCodeis missing:SetandMapbucket 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
Your
BaseScreenhas grown to 400 lines, six subclasses override half of it, and one change to the base broke three of them — what is the refactor?MediumStop 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
In
class Repo extends Base with Caching, Logging, both mixins overridefetchand callsuper.fetch()— whose body runs first, and why does swapping them change the result?HardLogging.fetchruns first, becausewithis 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
A base constructor calls an overridable
onInit(), and inside it the subclass'slate finalfield throws LateInitializationError — what is Dart's construction order?HardThe 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(); } …