Does Flutter/Dart support interface classes? How do interfaces work in Dart?
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
interfaceabstract classimplementsHow 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
implementsextends vs implements vs with
Comparison Table
| Keyword | Purpose | Inheritance | Multiple | Implementation |
|---|---|---|---|---|
| extends | Inheritance | Inherits code | ❌ Single only | Optional override |
| implements | Interface | No inheritance | ✅ Multiple | Must implement all |
| with | Mixin | Inherits code | ✅ Multiple | Automatic |
Example
dartabstract 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):
dartabstract 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:
dartabstract 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
interfacedart// 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 interface
- You want to prevent inheritance (only allow interface implementation)
- You're designing a library and want to enforce interface-only usage
Summary
| Question | Answer |
|---|---|
| Does Dart support interfaces? | ✅ Yes, via text text |
| Is there an text | ⚠️ 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
to define clear interfacestextabstract class
= no code inheritance - must re-implement all methodstextimplements
= code inheritance - inherits method implementationstextextends
Multiple interfaces allowed, single inheritance only
Dart 3.0+ added
modifier for library designtextinterface