Classes & Mixins

Dart's approach to object-oriented programming with classes, mixins, and extensions.

Classes

class Person {
  String name;
  int birthYear;

  // Constructor
  Person(this.name, this.birthYear);

  // Named constructor
  Person.born(this.name) : birthYear = DateTime.now().year;

  // Getter
  int get age => DateTime.now().year - birthYear;

  // Method
  String greet() => 'Hello, I am $name';
}
var p = Person('Ada', 1985);
print(p.age);        // 41
print(p.greet());    // Hello, I am Ada

Initializer list

class Point {
  final double x;
  final double y;

  Point(double x, double y)
      : x = x.abs(),
        y = y.abs();
}

Redirecting constructors

class Person {
  String name;
  int birthYear;

  Person(this.name, this.birthYear);
  Person.unnamed(int birthYear) : this('Unknown', birthYear);
}

Factory constructors

class Logger {
  final String name;
  static final Map<String, Logger> _cache = {};

  Logger._internal(this.name);

  factory Logger(String name) {
    return _cache.putIfAbsent(name, () => Logger._internal(name));
  }
}

Const constructors

class ImmutablePoint {
  final double x;
  final double y;

  const ImmutablePoint(this.x, this.y);
}

var origin = const ImmutablePoint(0, 0);

Inheritance

Dart uses extends for single inheritance:

class Animal {
  String name;
  Animal(this.name);

  void speak() => print('$name makes a sound');
}

class Dog extends Animal {
  String breed;
  Dog(String name, this.breed) : super(name);

  @override
  void speak() => print('$name barks!');
}

Abstract classes

abstract class Shape {
  double area();

  void describe() {
    print('Area: ${area()}');
  }
}

class Circle extends Shape {
  final double radius;
  Circle(this.radius);

  @override
  double area() => 3.14159 * radius * radius;
}

Interfaces

Every class implicitly defines an interface. Use implements to create a class with the same API but different implementation:

class MockPerson implements Person {
  @override
  String get name => 'Mock';

  @override
  int get birthYear => 2000;

  @override
  String greet() => 'Mock greeting';
}

Mixins

Mixins share code between classes without inheritance:

mixin Logging {
  void log(String message) => print('[LOG] $message');
}

mixin Timestamp {
  String get timestamp => DateTime.now().toIso8601String();
}

class Service with Logging, Timestamp {
  void run() {
    log('Service started at $timestamp');
  }
}

Mixin with constraint

mixin Wag on Animal {
  void wag() => print('$name wags tail!');
}

class GoodDog extends Dog with Wag {
  GoodDog(String name, String breed) : super(name, breed);
}

Extensions

Add methods to existing types without modifying them:

extension StringX on String {
  bool get isEmail => contains('@') && contains('.');
  String capitalized => isEmpty ? this : '${this[0].toUpperCase()}${substring(1)}';
}

'hello'.capitalized;     // 'Hello'
'[email protected]'.isEmail; // true

Enums

enum Status { pending, active, done }

var s = Status.active;
s.name;                    // 'active'
s.index;                   // 1
Status.values;              // [Status.pending, Status.active, Status.done]

// Enhanced enums (Dart 2.17+)
enum Vehicle {
  car(tires: 4, speed: 120),
  bicycle(tires: 2, speed: 25),
  motorcycle(tires: 2, speed: 180);

  final int tires;
  final int speed;
  const Vehicle({required this.tires, required this.speed});

  String get description => '$name has $tires tires, top speed $speed mph';
}

Next: Async