Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

An abstract interface class in Dart is a class modifier combination that defines a contract strictly meant for implementation rather than inheritance outside its defining library, while preventing direct instantiation via generative constructors. It merges the non-instantiability of an abstract class with the subtyping restrictions of an interface class.

Core Mechanics

The behavior of an abstract interface class is governed by strict compiler rules based on library boundaries:
  • Instantiation: Direct instantiation using generative constructors is prohibited globally. However, the class can define and be invoked via factory constructors that return instances of concrete subtypes (similar to Dart’s built-in List).
  • Implementation (implements): Permitted globally. Any class, regardless of the library it resides in, can implement the class’s interface.
  • Inheritance (extends): Permitted only within the same defining library. External libraries are blocked from extending the class.
  • Member Definitions: Can contain both abstract members (without bodies) and concrete members (with bodies).

Syntax and Cross-Library Behavior

The following example demonstrates the structural enforcement of the abstract interface modifiers across different Dart libraries. File: library_a.dart (Defining Library)
abstract interface class DataRepository {
  // Explicit generative constructor required for subclasses to extend
  // because declaring a factory constructor prevents implicit generation.
  DataRepository(); 

  // ALLOWED: Factory constructors can instantiate and return concrete subtypes.
  factory DataRepository.local() = LocalRepository;

  // Abstract method
  void fetch(); 

  // Concrete method
  void dispose() {
    print('Disposing resources');
  }
}

// ALLOWED: Extending is permitted within the same library.
// Inherits the concrete implementation of dispose().
class LocalRepository extends DataRepository {
  @override
  void fetch() => print('Fetching locally');
}
File: library_b.dart (Consuming Library)
import 'library_a.dart';

void main() {
  // ERROR: Cannot be instantiated via a generative constructor.
  // DataRepository repo = DataRepository();
  
  // ALLOWED: Instantiation via a factory constructor.
  DataRepository repo = DataRepository.local();
}

// ERROR: The 'interface' modifier prevents extension outside the defining library.
// class RemoteRepository extends DataRepository {}

// ALLOWED: External classes must use 'implements'.
class CloudRepository implements DataRepository {
  @override
  void fetch() {
    print('Fetching from cloud');
  }

  // REQUIRED: 'implements' only inherits member signatures.
  // The concrete dispose() behavior from DataRepository is not inherited.
  // It must be explicitly overridden.
  @override
  void dispose() {
    print('Disposing cloud resources');
  }
}

Technical Characteristics

  1. Interface-Only Inheritance: When an external class uses the implements keyword on an abstract interface class, the implements keyword tells Dart to inherit only the class’s interface (its member signatures). The implementing class does not inherit any concrete behavior defined in the base class and must provide its own implementation for every member (methods, getters, setters).
  2. Library Encapsulation: In Dart, a library is typically a single .dart file (along with any files included via part directives). The extends restriction of the interface modifier is enforced exactly at this boundary.
  3. Pattern Matching Exhaustiveness: Unlike sealed classes, an abstract interface class does not provide exhaustiveness checking for the compiler. A switch statement evaluating subtypes of an abstract interface class will require a default case or wildcard, as the compiler cannot guarantee all possible implementations are known.
Master Dart with Deep Grasping Methodology!Learn More