Watch the video (12 min)
On this page22 sections
  1. Prerequisites
  2. Connection With Previous Lectures
  3. Learning Objectives
  4. 1. What an Interface Is
  5. 2. Implementing a Class with implements
  6. 3. No Code Is Inherited
  7. 4. Fields Are Part of the Interface
  8. 5. Implementing Several Interfaces
  9. 6. An Abstract Class as a Pure Interface
  10. 7. The Object Has the Interface's Type
  11. 8. extends vs implements
  12. 9. Combining extends and implements
  13. 10. A Real-World Example
  14. Code Examples
  15. Common Beginner Mistakes
  16. Best Practices
  17. Mini Challenge
  18. Practice Questions
  19. Practice Answers
  20. Quick Revision
  21. Key Takeaways
  22. What's Next?

Lecture 63 — Interfaces

Category10 — Advanced OOP
Lecture063 of 140
Estimated video length12 min
TargetDart 3.12.2
LevelIntermediate

A class can extend only one parent. With implements, a class promises to do everything another class can do — for as many classes as it likes — and writes the code itself.

Contents

Prerequisites · Connection · Objectives · Lesson · Code Examples · Common Mistakes · Best Practices · Mini Challenge · Practice Questions · Answers · Takeaways

Every error message in this lecture is real. Each one was produced by running the code shown on Dart 3.12.2, so you will recognize it when you meet it yourself.


Prerequisites

Students should already understand, from Lectures 052–062:

  • classes, fields, methods, constructors, getters, and setters
  • extends, @override, and super
  • is checks and parent-typed variables
  • abstract classes and abstract methods

Connection With Previous Lectures

In Lecture 059 we learned that a Dart class has exactly one superclass. Now picture an office photocopier. It prints and it scans. We already have both classes:

dart
class Printer {
  void printPage(String text) {
    print('Printing: $text');
  }
}

class Scanner {
  void scan() {
    print('Scanning...');
  }
}

class Photocopier extends Printer, Scanner {}
output
Error: Each class definition can have at most one extends clause.
Try choosing one superclass and define your class to implement (or mix in) the others.

Dart refuses — and the message itself points to the answer: implement the others.


Learning Objectives

By the end of this lecture you will be able to:

  • Explain what an interface is and why every Dart class has one
  • Make a class follow an interface with implements
  • Explain why implements inherits no code at all
  • Provide fields, getters, and setters that an interface requires
  • Implement several interfaces in one class
  • Use an abstract class as a pure interface
  • Choose between extends and implements, and combine them in one class
  • Fix the errors Dart reports when an implementation is incomplete

1. What an Interface Is

An interface is the list of public members a class offers — the names of its fields, getters, setters, and methods, with their types. It says what an object can do, not how.

Analogy — a job advert lists duties: "answers phones, books meetings". Anyone who can do every duty can fill the job, however they learned it. The advert is the interface; each worker is an implementation.

In Dart, every class automatically has an interface made of its public members. There is no separate thing to declare.


2. Implementing a Class with implements

class B implements A means "a B can do everything an A can do":

dart
class Printer {
  void printPage(String text) {
    print('Printing: $text');
  }
}

class LaserPrinter implements Printer {
  @override
  void printPage(String text) {
    print('Laser printing: $text');
  }
}

void main() {
  LaserPrinter().printPage('Report');
}
output
Laser printing: Report

Each member of the interface is written with @override, exactly like an override from Lecture 060.


3. No Code Is Inherited

extends copies the parent's code into the child. implements copies nothing — only the promise. Leave a method out and Dart stops you:

dart
class LaserPrinter implements Printer {}
output
Error: The non-abstract class 'LaserPrinter' is missing implementations for these members:
 - Printer.printPage
Try to either
 - provide an implementation,
 - inherit an implementation from a superclass or mixin,
 - mark the class as abstract, or
 - provide a 'noSuchMethod' implementation.

For the same reason, super.printPage(text) does not work inside LaserPrinter: its superclass is still Object, which has no printPage.

output
Error: Superclass has no method named 'printPage'.

4. Fields Are Part of the Interface

A field gives a class a getter (and, if it is not final, a setter). Both belong to the interface, so the implementing class must provide them:

dart
class Person {
  Person(this.name);
  String name;
}

class Robot implements Person {
  @override
  String get name => 'R2';
}
output
Error: The non-abstract class 'Robot' is missing implementations for these members:
 - Person.name

A getter alone is not enough — name can also be assigned, so a setter is missing. The simplest fix is a field of our own, which supplies both:

dart
class Person {
  Person(this.name);
  String name;
}

class Robot implements Person {
  Robot(this.name);

  @override
  String name;
}

void main() {
  var robot = Robot('R2');
  robot.name = 'R3';
  print(robot.name);
}
output
R3

Notice that Robot writes its own constructor. Constructors are not part of an interface, so Person's constructor is never called.


5. Implementing Several Interfaces

A class can implement as many interfaces as it needs, separated by commas. This is how the photocopier works:

dart
class Printer {
  void printPage(String text) {
    print('Printing: $text');
  }
}

class Scanner {
  void scan() {
    print('Scanning...');
  }
}

class Photocopier implements Printer, Scanner {
  @override
  void printPage(String text) {
    print('Copier prints: $text');
  }

  @override
  void scan() {
    print('Copier scans the page');
  }
}

void main() {
  var copier = Photocopier();
  copier.scan();
  copier.printPage('Invoice');
}
output
Copier scans the page
Copier prints: Invoice

6. An Abstract Class as a Pure Interface

Implementing a normal class throws its code away, which feels wasteful. The usual style is an abstract class with no code at all — only abstract members, as in Lecture 062:

dart
abstract class Payable {
  double get amount;
  void pay();
}

class Bill implements Payable {
  Bill(this.amount);

  @override
  final double amount;

  @override
  void pay() {
    print('Paid bill of $amount');
  }
}

void main() {
  Bill(2500).pay();
}
output
Paid bill of 2500.0

Payable describes a role, and nothing is lost by implementing it. The abstract double get amount needs only a getter, so a final field satisfies it.

Dart also has an interface class modifier that controls how a class may be used from other files. We will study it in Lecture 130 — Class Modifiers.


7. The Object Has the Interface's Type

An object of Bill is a Payable. It passes is checks and fits any variable or parameter of that type:

dart
abstract class Payable {
  double get amount;
}

class Bill implements Payable {
  Bill(this.amount);
  @override
  final double amount;
}

void showAmount(Payable item) {
  print('Amount due: ${item.amount}');
}

void main() {
  Object thing = Bill(800);
  print(thing is Payable);
  showAmount(Bill(800));
}
output
true
Amount due: 800.0

showAmount accepts anything that is Payable, whatever its class. Using one type for many classes is polymorphism — Lecture 065.


8. extends vs implements

extendsimplements
How many?exactly one classany number
Code inherited?yes — fields and method bodiesno — you write every member
super.method()calls the parent's codenot available
Parent constructormust be callednot involved
Meaning"is a kind of, and works like""can do the same things as"

Use extends to reuse code from a close relative. Use implements to promise behaviour without taking anyone's code.


9. Combining extends and implements

One class can do both — extends comes first:

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

abstract class Swimmer {
  void swim();
}

abstract class Flyer {
  void fly();
}

class Duck extends Animal implements Swimmer, Flyer {
  Duck(super.name);

  @override
  void swim() => print('$name paddles');

  @override
  void fly() => print('$name flies');
}

void main() {
  var duck = Duck('Donald');
  duck.swim();
  duck.fly();
}
output
Donald paddles
Donald flies

Duck inherits name from Animal and promises the Swimmer and Flyer roles. Writing the clauses the other way round is an error:

output
Error: The extends clause must be before the implements clause.

10. A Real-World Example

A shop where different items share the same roles:

dart
abstract class Discountable {
  double discountedPrice(double rate);
}

abstract class Printable {
  String get label;
}

class Product {
  Product(this.name, this.price);
  final String name;
  final double price;
}

class Book extends Product implements Discountable, Printable {
  Book(super.name, super.price);

  @override
  double discountedPrice(double rate) => price - price * rate;

  @override
  String get label => 'Book: $name';
}

void main() {
  var book = Book('Dart Basics', 1200);
  print(book.label);
  print('After 25% off: ${book.discountedPrice(0.25)}');
}
output
Book: Dart Basics
After 25% off: 900.0

Book reuses name and price from Product with extends, and promises two independent roles with implements, writing each member itself.


Code Examples

Example 1 — Implementing a Class

dart
class Greeter {
  void greet() => print('Hello');
}

class LoudGreeter implements Greeter {
  @override
  void greet() => print('HELLO!');
}

void main() {
  LoudGreeter().greet();
}
output
HELLO!

Example 2 — A Getter from the Interface

dart
abstract class Named {
  String get name;
}

class Student implements Named {
  @override
  String get name => 'Zayan';
}

void main() {
  print(Student().name);
}
output
Zayan

Example 3 — A Field Satisfies Getter and Setter

dart
class Counter {
  int count = 0;
}

class StepCounter implements Counter {
  @override
  int count = 0;
}

void main() {
  var steps = StepCounter();
  steps.count = 5000;
  print(steps.count);
}
output
5000

Example 4 — Two Interfaces

dart
abstract class Reader {
  void read();
}

abstract class Writer {
  void write();
}

class Notebook implements Reader, Writer {
  @override
  void read() => print('Reading notes');

  @override
  void write() => print('Writing notes');
}

void main() {
  var notebook = Notebook();
  notebook.write();
  notebook.read();
}
output
Writing notes
Reading notes

Example 5 — An is Check

dart
abstract class Chargeable {
  void charge();
}

class Phone implements Chargeable {
  @override
  void charge() => print('Phone charging');
}

void main() {
  Object device = Phone();
  print(device is Chargeable);
  Phone().charge();
}
output
true
Phone charging

Example 6 — extends and implements Together

dart
class Vehicle {
  void start() => print('Engine on');
}

abstract class Electric {
  int get batteryLevel;
}

class ElectricCar extends Vehicle implements Electric {
  @override
  int get batteryLevel => 80;
}

void main() {
  var car = ElectricCar();
  car.start();
  print('Battery: ${car.batteryLevel}%');
}
output
Engine on
Battery: 80%

start comes from Vehicle; batteryLevel had to be written.

Example 7 — Student Marks

dart
abstract class Gradable {
  int get marks;
  String get grade;
}

class Student implements Gradable {
  Student(this.name, this.marks);
  final String name;

  @override
  final int marks;

  @override
  String get grade => marks >= 80 ? 'A' : 'B';
}

void main() {
  var ali = Student('Ali', 85);
  print('${ali.name}: ${ali.marks} (${ali.grade})');
}
output
Ali: 85 (A)

How it works

  1. Gradable promises two getters and contains no code.
  2. The final field marks satisfies int get marks.
  3. grade is a calculated getter, written in Student itself.

Example 8 — Shopping Total

dart
abstract class Priced {
  double get total;
}

class CartItem implements Priced {
  CartItem(this.price, this.quantity);
  final double price;
  final int quantity;

  @override
  double get total => price * quantity;
}

void main() {
  var item = CartItem(450, 3);
  print('Total: ${item.total}');
}
output
Total: 1350.0

How it works

  1. Any Priced object must be able to report a total.
  2. CartItem decides how: price times quantity.

Example 9 — Bank Balance

dart
abstract class Account {
  double get balance;
  void deposit(double amount);
}

class SavingsAccount implements Account {
  double _balance = 0;

  @override
  double get balance => _balance;

  @override
  void deposit(double amount) {
    _balance += amount;
  }
}

void main() {
  var sara = SavingsAccount();
  sara.deposit(1500);
  sara.deposit(500);
  print('Balance: ${sara.balance}');
}
output
Balance: 2000.0

How it works

  1. The interface asks for a balance getter only, so a private field plus a getter keeps the balance read-only from outside (Lecture 057).
  2. deposit is the only way to change it.

Example 10 — Temperature Sensors

dart
abstract class Sensor {
  double read();
}

class IndoorSensor implements Sensor {
  @override
  double read() => 24.5;
}

class OutdoorSensor implements Sensor {
  @override
  double read() => 31.0;
}

void report(Sensor sensor) {
  print('Temperature: ${sensor.read()}');
}

void main() {
  report(IndoorSensor());
  report(OutdoorSensor());
}
output
Temperature: 24.5
Temperature: 31.0

How it works

  1. Two unrelated classes implement the same interface.
  2. report accepts any Sensor, and each object runs its own read.

Common Beginner Mistakes

1. Expecting implements to inherit code

dart
class Printer {
  void printPage(String text) => print(text);
}

// class LaserPrinter implements Printer {} // ✗ printPage is missing
output
Error: The non-abstract class 'LaserPrinter' is missing implementations for these members:
 - Printer.printPage

Write every member yourself — or use extends if you really want the code.

2. Providing only a getter for a non-final field

dart
class Person {
  Person(this.name);
  String name;
}

// class Robot implements Person {
//   @override
//   String get name => 'R2'; // ✗ the setter is still missing
// }
output
Error: The non-abstract class 'Robot' is missing implementations for these members:
 - Person.name

Declare a field String name (or add a setter too).

3. Calling super in an implementing class

dart
class LaserPrinter implements Printer {
  @override
  void printPage(String text) {
    // super.printPage(text); // ✗ there is no parent code to call
    print('Laser: $text');
  }
}
output
Error: Superclass has no method named 'printPage'.

4. Using super parameters with implements

dart
class Person {
  Person(this.name);
  final String name;
}

// class Robot implements Person {
//   Robot(super.name); // ✗ Person's constructor is not involved
// }
output
Error: The super constructor has no corresponding positional parameter.

Use this.name and declare the field: Robot(this.name); @override final String name;.

5. Writing implements before extends

dart
// class Duck implements Swimmer extends Animal {} // ✗
output
Error: The extends clause must be before the implements clause.

6. Trying to extend two classes

dart
// class Photocopier extends Printer, Scanner {} // ✗
output
Error: Each class definition can have at most one extends clause.

Extend one at most, implement the rest.


Best Practices

  • Describe roles with abstract classes that contain no code, then implement them.
  • Use extends to reuse code, implements to promise behaviour.
  • Write @override on every member you implement.
  • Keep interfaces small — one role each (Printable, Payable) — and combine them.
  • Name roles after what the object can do: Chargeable, Discountable, Sensor.
  • Write parameter types as the interface (void report(Sensor sensor)) so any implementation fits.

Mini Challenge

Create an abstract class Shape with a getter double get area. Write Square (side 4) and Circle (radius 1, use 3.14) that implement it, and a function printArea(Shape s).

Your program should print exactly:

output
Area: 16.0
Area: 3.14

Try it yourself before opening the solution.

Show solution
dart
abstract class Shape {
  double get area;
}

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

  @override
  double get area => side * side;
}

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

  @override
  double get area => 3.14 * radius * radius;
}

void printArea(Shape s) {
  print('Area: ${s.area}');
}

void main() {
  printArea(Square(4));
  printArea(Circle(1));
}
output
Area: 16.0
Area: 3.14

How it works

  1. Shape lists one getter and has no code.
  2. Each class writes its own area.
  3. printArea accepts both because both are Shapes.

Practice Questions

Work through these in order — they run from beginner to application to challenge. Every question gives you exact values, so you can compare your output with the answers.

Beginner

  1. Write a class Bell with a method ring() that prints Ring. Write DoorBell implements Bell whose ring() prints Ding dong, and call it.
  2. Create an abstract class Named with String get name. Implement it in City, returning 'Lahore', and print the name.
  3. In one sentence each: what does extends inherit, and what does implements inherit?

Application

  1. This class does not compile. Name the missing member and fix it:

    dart
    class Pet {
      Pet(this.name);
      String name;
    }
    
    class RobotPet implements Pet {}

    Then create RobotPet('Bolt') and print its name.

  2. Create abstract classes Walker (void walk()) and Talker (void talk()). Write Child implements Walker, Talker, with outputs Zayan walks and Zayan talks.

  3. Write an abstract class Scored with int get score. Implement it in Game with a final field, and print Score: 70 for Game(70).

  4. Write a function void describe(Scored s) that prints Score is 70 and call it with the Game from Question 6.

Challenge

  1. Write class Employee with a final String name, and an abstract class Payable with double get salary. Write Manager extends Employee implements Payable with a fixed salary of 90000. Print Sara earns 90000.0.
  2. Write an abstract class Wallet with double get balance and void spend(double amount). Implement it so spending more than the balance prints Not enough money and leaves the balance unchanged. Start at 500, spend 200, then 400, and print the balance.
  3. Write abstract classes Heater (void heat()) and Thermometer (double get temperature). Write SmartKettle implements Heater, Thermometer: it starts at 20.0 and every heat() adds 40.0. Heat it twice and print Temperature: 100.0.

Practice Answers

Question 1 — Solution

dart
class Bell {
  void ring() => print('Ring');
}

class DoorBell implements Bell {
  @override
  void ring() => print('Ding dong');
}

void main() {
  DoorBell().ring();
}
output
Ding dong

Question 2 — Solution

dart
abstract class Named {
  String get name;
}

class City implements Named {
  @override
  String get name => 'Lahore';
}

void main() {
  print(City().name);
}
output
Lahore

Question 3 — Solution

extends inherits the parent's fields and method bodies. implements inherits no code — only the obligation to provide every member of the interface.

Question 4 — Solution

Pet.name — both its getter and its setter — is missing. A field provides both:

dart
class Pet {
  Pet(this.name);
  String name;
}

class RobotPet implements Pet {
  RobotPet(this.name);

  @override
  String name;
}

void main() {
  print(RobotPet('Bolt').name);
}
output
Bolt

Question 5 — Solution

dart
abstract class Walker {
  void walk();
}

abstract class Talker {
  void talk();
}

class Child implements Walker, Talker {
  @override
  void walk() => print('Zayan walks');

  @override
  void talk() => print('Zayan talks');
}

void main() {
  var child = Child();
  child.walk();
  child.talk();
}
output
Zayan walks
Zayan talks

Question 6 — Solution

dart
abstract class Scored {
  int get score;
}

class Game implements Scored {
  Game(this.score);

  @override
  final int score;
}

void main() {
  print('Score: ${Game(70).score}');
}
output
Score: 70

Question 7 — Solution

Add this function to Question 6 and change main:

dart
void describe(Scored s) {
  print('Score is ${s.score}');
}

void main() {
  describe(Game(70));
}
output
Score is 70

Question 8 — Solution

dart
class Employee {
  Employee(this.name);
  final String name;
}

abstract class Payable {
  double get salary;
}

class Manager extends Employee implements Payable {
  Manager(super.name);

  @override
  double get salary => 90000;
}

void main() {
  var sara = Manager('Sara');
  print('${sara.name} earns ${sara.salary}');
}
output
Sara earns 90000.0

Question 9 — Solution

dart
abstract class Wallet {
  double get balance;
  void spend(double amount);
}

class CashWallet implements Wallet {
  CashWallet(this._balance);
  double _balance;

  @override
  double get balance => _balance;

  @override
  void spend(double amount) {
    if (amount > _balance) {
      print('Not enough money');
      return;
    }
    _balance -= amount;
  }
}

void main() {
  var wallet = CashWallet(500);
  wallet.spend(200);
  wallet.spend(400);
  print('Balance: ${wallet.balance}');
}
output
Not enough money
Balance: 300.0

Question 10 — Solution

dart
abstract class Heater {
  void heat();
}

abstract class Thermometer {
  double get temperature;
}

class SmartKettle implements Heater, Thermometer {
  @override
  double temperature = 20.0;

  @override
  void heat() {
    temperature += 40.0;
  }
}

void main() {
  var kettle = SmartKettle();
  kettle.heat();
  kettle.heat();
  print('Temperature: ${kettle.temperature}');
}
output
Temperature: 100.0

A field may implement a getter-only member; the extra setter does no harm.


Quick Revision

  1. What is an interface?
  2. Which Dart classes have an interface?
  3. What code does implements inherit?
  4. Why must a non-final field be implemented with both a getter and a setter?
  5. How many classes can a class extend? How many can it implement?
  6. Why is an abstract class with no code the usual way to write an interface?
  7. In class A extends B implements C, D, which class provides code?

Key Takeaways

  • An interface is a class's public members — what it can do, not how.
  • Every class has an implicit interface; no extra declaration is needed.
  • implements inherits no code: every method, getter, and setter must be written.
  • A field in the interface means a getter (and a setter, unless final) to provide.
  • super calls and the parent's constructor play no part in implements.
  • A class can implement many interfaces but extend only one class.
  • Abstract classes with no code are the natural way to describe a role.
  • class A extends B implements C, D reuses B's code and promises C and D.

What's Next?

Lecture 64 — Mixins. implements lets a class take on many roles, but it must write every member itself — even when ten classes need the same code. Next we will learn how mixins share ready-made code across classes that have no common parent.


Video lecture

Subscribe
Placeholder video — the real recording isn't linked yet.