flutter
/

Dart Abstract Classes – A Complete Guide

Last Sync: Today

On this page

11
0%
5 min read
Remaining
5 minleft

Click any section to jump — progress syncs automatically

flutter

Dart Abstract Classes – A Complete Guide

What are Abstract Classes?

An abstract class is a class that cannot be instantiated directly. It is designed to be a blueprint for other classes. Abstract classes may contain both abstract methods (methods without a body) and concrete methods (with implementation). They are declared using the abstract keyword. Abstract classes are essential for defining common behavior that subclasses can share while enforcing that certain methods are implemented by the subclasses.

Defining an Abstract Class

To declare an abstract class, add the abstract keyword before the class keyword. Abstract classes can have constructors, fields, and both abstract and concrete methods.

DARTRead-only
1
abstract class Animal {
  String name;

  Animal(this.name);

  // Abstract method – no body
  void makeSound();

  // Concrete method
  void sleep() {
    print('$name is sleeping.');
  }
}

class Dog extends Animal {
  Dog(String name) : super(name);

  @override
  void makeSound() {
    print('$name barks.');
  }
}

void main() {
  // Animal animal = Animal('pet'); // Error: Cannot instantiate abstract class
  Dog dog = Dog('Buddy');
  dog.makeSound(); // Buddy barks.
  dog.sleep();     // Buddy is sleeping.
}

Abstract Methods

An abstract method is a method declared without an implementation. It must be overridden by any non‑abstract subclass. Abstract methods can only exist inside abstract classes. They define a contract that subclasses must fulfill.

DARTRead-only
1
abstract class Shape {
  double area(); // abstract method

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

class Circle extends Shape {
  double radius;

  Circle(this.radius);

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

void main() {
  Circle circle = Circle(5);
  circle.display(); // Area: 78.53975
}

Constructors in Abstract Classes

Even though you cannot instantiate an abstract class, it can still have constructors. These constructors are called when a concrete subclass is instantiated. They are useful for initializing fields defined in the abstract class.

DARTRead-only
1
abstract class Vehicle {
  String brand;

  Vehicle(this.brand);

  void start();
}

class Car extends Vehicle {
  String model;

  Car(String brand, this.model) : super(brand);

  @override
  void start() {
    print('$brand $model is starting.');
  }
}

void main() {
  Car car = Car('Toyota', 'Camry');
  car.start(); // Toyota Camry is starting.
}

Abstract Classes vs Interfaces

In Dart, every class defines an interface, so the line between abstract classes and interfaces can be blurry. However, there are important differences:

    • An abstract class can have fields and concrete methods; an interface (via implements) cannot provide any implementation.
    • A class can implement multiple interfaces but can extend only one abstract class.
    • Use an abstract class when you want to share code among several closely related classes. Use interfaces (implicit or explicit) when you want to define a capability that can be implemented by unrelated classes.

Factory Constructors in Abstract Classes

Abstract classes can have factory constructors. This is useful when you want to return an instance of a concrete subclass based on some input, while keeping the abstract class as the only visible type.

DARTRead-only
1
abstract class Shape {
  factory Shape(String type) {
    if (type == 'circle') return Circle(5);
    if (type == 'square') return Square(4);
    throw ArgumentError('Unknown shape type');
  }

  double area();
}

class Circle implements Shape {
  double radius;
  Circle(this.radius);
  @override
  double area() => 3.14 * radius * radius;
}

class Square implements Shape {
  double side;
  Square(this.side);
  @override
  double area() => side * side;
}

void main() {
  Shape shape = Shape('circle');
  print(shape.area()); // 78.5
}

Abstract Classes and Polymorphism

Abstract classes are often used as base types for polymorphism. You can write functions that accept the abstract type, and at runtime they work with any concrete subclass.

DARTRead-only
1
abstract class PaymentProcessor {
  void processPayment(double amount);
}

class CreditCardProcessor extends PaymentProcessor {
  @override
  void processPayment(double amount) {
    print('Processing credit card payment of \$$amount');
  }
}

class PayPalProcessor extends PaymentProcessor {
  @override
  void processPayment(double amount) {
    print('Processing PayPal payment of \$$amount');
  }
}

void main() {
  List<PaymentProcessor> processors = [
    CreditCardProcessor(),
    PayPalProcessor(),
  ];

  for (var processor in processors) {
    processor.processPayment(100.0);
  }
}

Common Mistakes

    • Trying to instantiate an abstract class: This results in a compile error.
    • Forgetting to override an abstract method: The subclass must override all abstract methods; otherwise, it must also be declared abstract.
    • Using implements when you meant extends: If you want to reuse implementation, use extends; if you only want to satisfy a contract, use implements.

Best Practices

    • Use abstract classes to define a common base with shared fields and methods.
    • Keep abstract classes focused – they should represent a clear concept (e.g., Animal, Shape, PaymentProcessor).
    • Prefer composition over inheritance when appropriate, but abstract classes are a good choice for true 'is‑a' relationships.
    • Document abstract methods clearly so implementers know what behavior is expected.

Complete Example

DARTRead-only
1
abstract class Employee {
  String name;
  double salary;

  Employee(this.name, this.salary);

  void work();

  void displayInfo() {
    print('Name: $name, Salary: \$$salary');
  }
}

class Manager extends Employee {
  int teamSize;

  Manager(String name, double salary, this.teamSize) : super(name, salary);

  @override
  void work() {
    print('$name is managing a team of $teamSize people.');
  }

  void conductMeeting() {
    print('$name is conducting a meeting.');
  }
}

class Developer extends Employee {
  String programmingLanguage;

  Developer(String name, double salary, this.programmingLanguage)
      : super(name, salary);

  @override
  void work() {
    print('$name is writing code in $programmingLanguage.');
  }
}

void main() {
  List<Employee> employees = [
    Manager('Alice', 80000, 5),
    Developer('Bob', 70000, 'Dart'),
  ];

  for (var employee in employees) {
    employee.displayInfo();
    employee.work();
    if (employee is Manager) {
      employee.conductMeeting();
    }
    print('---');
  }
}

Key Takeaways

    • Abstract classes (declared with abstract) cannot be instantiated.
    • They may contain both abstract methods (no body) and concrete methods (with implementation).
    • Subclasses must override all abstract methods, or they must also be abstract.
    • Abstract classes can have constructors, fields, and factory constructors.
    • Use abstract classes when you have a base type with shared implementation and you want to enforce that certain methods are implemented by subclasses.

Try it yourself

abstract class Animal {
  void speak();
  void sleep() {
    print('Sleeping...');
  }
}

class Dog extends Animal {
  @override
  void speak() {
    print('Woof!');
  }
}

void main() {
  var dog = Dog();
  dog.speak();
  dog.sleep();
}

Test Your Knowledge

Q1
of 4

Which keyword is used to declare an abstract class in Dart?

A
abstract
B
interface
C
base
D
virtual
Q2
of 4

Can you create an instance of an abstract class using `new`?

A
Yes
B
No
C
Only if it has no abstract methods
D
Only with a factory constructor
Q3
of 4

What must a concrete subclass do when it extends an abstract class?

A
Override all concrete methods
B
Override all abstract methods
C
Implement at least one method
D
Call the super constructor
Q4
of 4

Can an abstract class have fields?

A
Yes
B
No

Frequently Asked Questions

Can an abstract class have both abstract and non‑abstract methods?

Yes, that's one of the main features of abstract classes. They can provide default implementations for some methods while leaving others abstract for subclasses to implement.

Can an abstract class be instantiated using a factory constructor?

You cannot instantiate an abstract class directly, but you can define a factory constructor that returns an instance of a concrete subclass. The caller uses the abstract class type, but the object created is a concrete subclass.

What happens if a subclass does not override all abstract methods?

If a subclass does not override all abstract methods, it must itself be declared abstract. Otherwise, you'll get a compile‑time error.

Can an abstract class implement an interface?

Yes, an abstract class can implement one or more interfaces using implements. It can then either implement the required methods or leave them abstract for subclasses to implement.

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

In Dart, every class defines an interface, so the line is subtle. An abstract class can provide default implementations and have fields, while an interface (via implements) forces the implementing class to provide all implementations. Also, a class can implement multiple interfaces but extend only one abstract class.

Can an abstract class have a constructor?

Yes, abstract classes can have constructors. They are used to initialize fields and are called when a concrete subclass is instantiated.

Previous

dart interfaces

Next

dart lambdas

Related Content

Need help?

Explore our comprehensive docs or start a chat with our tech experts.