Skip to content
AgileLogics
6 min read

Inheritance

Prerequisites: Constructors

Inheritance lets a class build on another class. The subclass gets the superclass's fields and methods and can add or change behavior.

Example

class Vehicle {
  final int wheels;
  Vehicle(this.wheels);

  String describe() => 'A vehicle with $wheels wheels';
}

class Bicycle extends Vehicle {
  Bicycle() : super(2);

  @override
  String describe() => '${super.describe()} that you pedal';
}

class Car extends Vehicle {
  final String brand;
  Car(this.brand) : super(4);

  @override
  String describe() => 'A $brand car';
}

void main() {
  final List<Vehicle> vehicles = [Bicycle(), Car('Volvo')];
  for (final vehicle in vehicles) {
    print(vehicle.describe());
  }
}

Expected output

A vehicle with 2 wheels that you pedal
A Volvo car
  • extends Vehicle makes the class a subclass.
  • : super(2) calls the superclass constructor.
  • @override marks a method that replaces the superclass's version. It's optional to the compiler, but the recommended lints require it, and it catches typos.
  • super.describe() calls the superclass's version.
  • A List<Vehicle> can hold any subclass, and each element uses its own describe() — this is polymorphism.

Super parameters

When a subclass just passes values up, super parameters avoid repeating them:

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

class Cat extends Animal {
  final bool indoor;
  Cat(super.name, {this.indoor = true});
}

void main() {
  final cat = Cat('Tom');
  print('${cat.name}, indoor: ${cat.indoor}');
}
Tom, indoor: true

Abstract classes

An abstract class can't be created directly, and can declare methods that subclasses must implement:

abstract class Shape {
  double area();
  String describe() => 'Area: ${area().toStringAsFixed(2)}';
}

class Square extends Shape {
  final double side;
  Square(this.side);

  @override
  double area() => side * side;
}

void main() {
  print(Square(3).describe());
}
Area: 9.00

Prefer composition

Deep inheritance chains become hard to follow. Dart code often shares behavior with interfaces and mixins instead — the next two lessons.