Skip to main content
An abstract class in Dart is a class declared with the abstract modifier that cannot be directly instantiated. It serves as a structural blueprint, allowing developers to define a strict contract of methods and properties that derived classes must implement, while optionally providing shared concrete state and behavior.

Syntax and Instantiation

To declare an abstract class, prepend the class keyword with abstract. Attempting to instantiate an abstract class using a generative constructor results in a compile-time error.
abstract class Vehicle {
  // Class body
}

void main() {
  // ERROR: Abstract classes can't be instantiated.
  // Vehicle myVehicle = Vehicle(); 
}

Abstract Methods

Abstract classes can contain abstract methods—method signatures that lack a body (implementation). These are defined by terminating the method signature with a semicolon instead of a code block. Any concrete class extending the abstract class is strictly required to provide an implementation for all inherited abstract methods.
abstract class DataRepository {
  // Abstract methods: No body, ends with a semicolon
  void save(String data);
  String fetch();
}

Concrete Members

Unlike pure interfaces in some other languages, Dart abstract classes can contain concrete state and behavior. They can declare fields, standard methods with bodies, getters, setters, and constructors.
abstract class NetworkClient {
  // Concrete field
  final String baseUrl;

  // Generative constructor
  NetworkClient(this.baseUrl);

  // Abstract method
  void connect();

  // Concrete method
  void disconnect() {
    print('Disconnected from $baseUrl');
  }
}

extends vs. implements

Because every class in Dart implicitly defines an interface, abstract classes can be utilized in two distinct ways by derived classes:
  1. Using extends (Inheritance): The subclass inherits the concrete methods and state of the abstract class. The subclass is only required to override the abstract methods. It must also call the abstract class’s constructor via super if a non-default constructor is defined.
    class RestClient extends NetworkClient {
      RestClient(String url) : super(url);
    
      @override
      void connect() {
        // Implementation required
      }
      // disconnect() is inherited and does not need to be overridden
    }
    
  2. Using implements (Interface Contract): The subclass treats the abstract class strictly as an interface. It inherits no implementation or state. The subclass must override all members defined in the abstract class, including concrete methods, getters, setters, and fields.
    class MockClient implements NetworkClient {
      // Must override the field
      @override
      final String baseUrl;
    
      MockClient(this.baseUrl);
    
      // Must override the abstract method
      @override
      void connect() {}
    
      // Must override the concrete method
      @override
      void disconnect() {}
    }
    

Factory Constructors in Abstract Classes

While an abstract class cannot be instantiated directly via a generative constructor, it can define a factory constructor. This allows the abstract class to return an instance of a concrete subclass when the constructor is invoked, bypassing the instantiation restriction.
abstract class Logger {
  // Factory constructor returning a concrete subclass
  factory Logger() => _ConsoleLogger();

  void log(String message);
}

class _ConsoleLogger implements Logger {
  @override
  void log(String message) => print(message);
}

void main() {
  // Valid: Invokes the factory constructor, returning _ConsoleLogger
  Logger myLogger = Logger(); 
}
Tired of Poor Dart Skills? Fix That With Deep Grasping!Learn More