Question #434MediumDart BasicsOOP Concepts

Does Flutter/Dart support interface classes? How do interfaces work in Dart?

#dart#interface#abstract-class#implements#oop

Answer

Quick Answer

Yes! Flutter/Dart fully supports interfaces, but in a unique way: Every class is implicitly an interface. Dart doesn't have a dedicated

text
interface
keyword like Java or C#. Instead, you use
text
abstract class
to define contracts and
text
implements
to implement them.


How Dart Implements Interfaces

Key Concept

dart
// In Dart, EVERY class can be used as an interface
class RegularClass {
  void method() {}
}

// Any class can implement RegularClass as an interface!
class MyClass implements RegularClass {
  
  void method() {
    // Must provide implementation
  }
}

Method 1: Using Abstract Classes (Recommended)

Abstract classes are the standard way to define interfaces in Dart:

dart
// Define interface using abstract class
abstract class Flyable {
  void fly();
  void land();
  double get altitude;
}

abstract class Swimmable {
  void swim();
  double get depth;
}

// Implement single interface
class Bird implements Flyable {
  
  double get altitude => 1000.0;

  
  void fly() => print('Bird is flying');

  
  void land() => print('Bird has landed');
}

// Implement multiple interfaces
class Duck implements Flyable, Swimmable {
  
  double get altitude => 50.0;

  
  double get depth => 2.0;

  
  void fly() => print('Duck is flying low');

  
  void land() => print('Duck landed on water');

  
  void swim() => print('Duck is swimming');
}

Method 2: Using Regular Classes as Interfaces

Any class can be used as an interface:

dart
// Regular class with implementation
class Animal {
  String name = 'Unknown';

  void eat() {
    print('$name is eating');
  }

  void sleep() {
    print('$name is sleeping');
  }
}

// Implement regular class as interface
class Robot implements Animal {
  
  String name = 'RobotX';

  
  void eat() {
    // Must re-implement (can't inherit)
    print('$name is recharging');
  }

  
  void sleep() {
    // Must re-implement (can't inherit)
    print('$name is in standby mode');
  }
}

void main() {
  final robot = Robot();
  robot.eat();    // RobotX is recharging
  robot.sleep();  // RobotX is in standby mode
}

Important: When using

text
implements
, you must re-implement all methods and fields, even if the original class had implementations.


extends vs implements vs with

Comparison Table

KeywordPurposeInheritanceMultipleImplementation
extendsInheritanceInherits code❌ Single onlyOptional override
implementsInterfaceNo inheritance✅ MultipleMust implement all
withMixinInherits code✅ MultipleAutomatic

Example

dart
abstract class Vehicle {
  void move();
}

mixin Electric {
  void charge() => print('Charging...');
}

class Car extends Vehicle with Electric {
  
  void move() => print('Car is moving');
}

class Drone implements Vehicle {
  
  void move() => print('Drone is flying');
  // Cannot use Electric mixin here with implements
}

Multiple Interfaces

Dart allows implementing multiple interfaces (unlike single inheritance):

dart
abstract class Readable {
  String read();
}

abstract class Writable {
  void write(String data);
}

abstract class Deletable {
  void delete();
}

// Implement all three interfaces
class File implements Readable, Writable, Deletable {
  String _content = '';

  
  String read() => _content;

  
  void write(String data) {
    _content = data;
  }

  
  void delete() {
    _content = '';
  }
}

Real-World Flutter Example: DAO Pattern

dart
// Define DAO interface
abstract class IUserDAO {
  Future<List<User>> getAllUsers();
  Future<User?> getUserById(int id);
  Future<void> insertUser(User user);
  Future<void> updateUser(User user);
  Future<void> deleteUser(int id);
}

// Implementation 1: Firebase
class FirebaseUserDAO implements IUserDAO {
  final FirebaseFirestore _firestore;

  FirebaseUserDAO(this._firestore);

  
  Future<List<User>> getAllUsers() async {
    final snapshot = await _firestore.collection('users').get();
    return snapshot.docs.map((doc) => User.fromMap(doc.data())).toList();
  }

  
  Future<User?> getUserById(int id) async {
    final doc = await _firestore.collection('users').doc(id.toString()).get();
    return doc.exists ? User.fromMap(doc.data()!) : null;
  }

  
  Future<void> insertUser(User user) async {
    await _firestore.collection('users').doc(user.id.toString()).set(user.toMap());
  }

  
  Future<void> updateUser(User user) async {
    await _firestore.collection('users').doc(user.id.toString()).update(user.toMap());
  }

  
  Future<void> deleteUser(int id) async {
    await _firestore.collection('users').doc(id.toString()).delete();
  }
}

// Implementation 2: SQLite
class SQLiteUserDAO implements IUserDAO {
  final Database _database;

  SQLiteUserDAO(this._database);

  
  Future<List<User>> getAllUsers() async {
    final List<Map<String, dynamic>> maps = await _database.query('users');
    return maps.map((map) => User.fromMap(map)).toList();
  }

  
  Future<User?> getUserById(int id) async {
    final result = await _database.query('users', where: 'id = ?', whereArgs: [id]);
    return result.isNotEmpty ? User.fromMap(result.first) : null;
  }

  
  Future<void> insertUser(User user) async {
    await _database.insert('users', user.toMap());
  }

  
  Future<void> updateUser(User user) async {
    await _database.update('users', user.toMap(), where: 'id = ?', whereArgs: [user.id]);
  }

  
  Future<void> deleteUser(int id) async {
    await _database.delete('users', where: 'id = ?', whereArgs: [id]);
  }
}

// Usage - Dependency Injection
class UserRepository {
  final IUserDAO _dao;

  // Can swap between Firebase and SQLite easily!
  UserRepository(this._dao);

  Future<List<User>> getUsers() => _dao.getAllUsers();
}

void main() {
  // Use Firebase
  final firebaseRepo = UserRepository(FirebaseUserDAO(FirebaseFirestore.instance));

  // Or use SQLite
  final sqliteRepo = UserRepository(SQLiteUserDAO(database));
}

Interface with Default Implementation (Abstract Class)

Abstract classes can provide default implementations:

dart
abstract class Shape {
  // Abstract methods (must be implemented)
  double calculateArea();
  double calculatePerimeter();

  // Concrete method with default implementation
  void display() {
    print('Area: ${calculateArea()}');
    print('Perimeter: ${calculatePerimeter()}');
  }
}

class Circle implements Shape {
  final double radius;

  Circle(this.radius);

  
  double calculateArea() => 3.14159 * radius * radius;

  
  double calculatePerimeter() => 2 * 3.14159 * radius;

  // display() is NOT inherited when using implements
  // Must re-implement it
  
  void display() {
    print('Circle:');
    print('  Radius: $radius');
    print('  Area: ${calculateArea()}');
    print('  Perimeter: ${calculatePerimeter()}');
  }
}

// To inherit default implementation, use extends instead
class Rectangle extends Shape {
  final double width;
  final double height;

  Rectangle(this.width, this.height);

  
  double calculateArea() => width * height;

  
  double calculatePerimeter() => 2 * (width + height);

  // display() is inherited automatically!
}

Best Practices

1. Use Abstract Classes for Interfaces

dart
Good - Clear intent:
abstract class IAuthService {
  Future<User> login(String email, String password);
  Future<void> logout();
}

Avoid - Using regular class:
class AuthService {
  Future<User> login(String email, String password) => throw UnimplementedError();
  Future<void> logout() => throw UnimplementedError();
}

2. Naming Convention

dart
Option 1 - Prefix with 'I':
abstract class IRepository { ... }

Option 2 - Use descriptive names:
abstract class Flyable { ... }
abstract class Drawable { ... }

Option 3 - Use 'able' suffix:
abstract class Clickable { ... }
abstract class Serializable { ... }

3. Keep Interfaces Focused

dart
Good - Single responsibility:
abstract class Readable {
  String read();
}

abstract class Writable {
  void write(String data);
}

class File implements Readable, Writable { ... }

Bad - Too many responsibilities:
abstract class FileOperations {
  String read();
  void write(String data);
  void delete();
  void copy(String destination);
  void move(String destination);
  Future<int> getSize();
  Future<DateTime> getModifiedDate();
}

4. Use Interfaces for Testability

dart
// Interface
abstract class IApiService {
  Future<User> fetchUser(int id);
}

// Real implementation
class ApiService implements IApiService {
  
  Future<User> fetchUser(int id) async {
    // Actual HTTP call
    final response = await http.get(Uri.parse('https://api.example.com/users/$id'));
    return User.fromJson(jsonDecode(response.body));
  }
}

// Mock implementation for testing
class MockApiService implements IApiService {
  
  Future<User> fetchUser(int id) async {
    return User(id: id, name: 'Test User');
  }
}

// Repository uses interface
class UserRepository {
  final IApiService _apiService;

  UserRepository(this._apiService);

  Future<User> getUser(int id) => _apiService.fetchUser(id);
}

// In tests:
void main() {
  test('UserRepository fetches user', () async {
    final mockApi = MockApiService();
    final repository = UserRepository(mockApi);

    final user = await repository.getUser(1);

    expect(user.name, 'Test User');
  });
}

Common Mistakes

Mistake 1: Expecting Code Inheritance with implements

dart
// ❌ WRONG ASSUMPTION
class Animal {
  void eat() => print('Eating');
}

class Dog implements Animal {
  // ERROR: Must override eat(), can't inherit it!
}

// ✅ CORRECT - Use extends for inheritance:
class Dog extends Animal {
  // eat() is inherited automatically
}

Mistake 2: Using implements with Concrete Classes Unnecessarily

dart
// ❌ WRONG - Forcing re-implementation when you want inheritance:
class Vehicle {
  void start() => print('Starting engine');
}

class Car implements Vehicle {
  
  void start() => print('Starting engine'); // Duplicated code!
}

// ✅ CORRECT - Use extends:
class Car extends Vehicle {
  // start() inherited, no duplication
}

Dart 3.0 Update: Interface Class Modifier

Dart 3.0 introduced explicit

text
interface
modifier:

dart
// Dart 3.0+ feature
interface class VehicleInterface {
  void move() {}
}

// Can only be implemented, NOT extended
class Car implements VehicleInterface {
  
  void move() => print('Car moving');
}

// ❌ This will ERROR in Dart 3.0+:
// class Bus extends VehicleInterface { }

When to use

text
interface
modifier:

  • You want to prevent inheritance (only allow interface implementation)
  • You're designing a library and want to enforce interface-only usage

Summary

QuestionAnswer
Does Dart support interfaces?✅ Yes, via
text
abstract class
and
text
implements
Is there an
text
interface
keyword?
⚠️ Not in Dart 2.x. Added as modifier in Dart 3.0+
Can I implement multiple interfaces?✅ Yes, unlimited
Can I extend multiple classes?❌ No, single inheritance only
Inherit code with implements?❌ No, must re-implement everything
Inherit code with extends?✅ Yes, full inheritance

Key Takeaways

Every class is an interface in Dart - you can implement any class

Use

text
abstract class
to define clear interfaces

text
implements
= no code inheritance - must re-implement all methods

text
extends
= code inheritance - inherits method implementations

Multiple interfaces allowed, single inheritance only

Dart 3.0+ added

text
interface
modifier for library design


Resources