Applying SOLID principles in Flutter (or Dart) helps you write cleaner, scalable, and maintainable code, especially as your app grows. Let’s go through each principle with Flutter-specific examples 👇
🧱 1. S — Single Responsibility Principle (SRP)
A class should have only one reason to change.
❌ Bad:
class UserService {
void saveUser(User user) {
// Save to database
}
void sendWelcomeEmail(User user) {
// Send email
}
}
This class handles both data persistence and email sending — two responsibilities.
✅ Good:
class UserRepository {
void saveUser(User user) {
// Save to database
}
}
class EmailService {
void sendWelcomeEmail(User user) {
// Send email
}
}
Now each class has one clear responsibility, making code easier to test and modify.
🧩 2. O — Open/Closed Principle (OCP)
Classes should be open for extension, but closed for modification.
❌ Bad:
class NotificationService {
void send(String type, String message) {
if (type == 'email') {
// send email
} else if (type == 'sms') {
// send sms
}
}
}
If you add a new notification type (like push), you must modify this class.
✅ Good (with polymorphism):
abstract class Notifier {
void send(String message);
}
class EmailNotifier implements Notifier {
@override
void send(String message) {
// send email
}
}
class SmsNotifier implements Notifier {
@override
void send(String message) {
// send sms
}
}
class NotificationService {
final Notifier notifier;
NotificationService(this.notifier);
void notify(String message) => notifier.send(message);
}
To add a new type (like PushNotifier), just create a new class — no need to modify existing ones.
⚙️ 3. L — Liskov Substitution Principle (LSP)
Subtypes must be substitutable for their base types.
❌ Bad:
class Bird {
void fly() {}
}
class Penguin extends Bird {
@override
void fly() {
throw Exception("Penguins can't fly!");
}
}
Penguin violates LSP because it cannot safely substitute Bird.
✅ Good:
abstract class Bird {}
abstract class FlyingBird extends Bird {
void fly();
}
class Sparrow extends FlyingBird {
@override
void fly() => print("Flying...");
}
class Penguin extends Bird {}
Now both Sparrow and Penguin fit their logical contracts.
🪄 4. I — Interface Segregation Principle (ISP)
Clients should not be forced to depend on methods they do not use.
❌ Bad:
abstract class Worker {
void work();
void eat();
}
class Robot implements Worker {
@override
void work() => print("Working...");
@override
void eat() {
throw Exception("Robots don't eat!");
}
}
✅ Good:
abstract class Workable {
void work();
}
abstract class Eatable {
void eat();
}
class Human implements Workable, Eatable {
@override
void work() => print("Working...");
@override
void eat() => print("Eating...");
}
class Robot implements Workable {
@override
void work() => print("Working...");
}
🧠 5. D — Dependency Inversion Principle (DIP)
High-level modules should not depend on low-level modules; both should depend on abstractions.
❌ Bad:
class ApiService {
final HttpClient _client = HttpClient();
void fetchData() {
_client.getUrl(Uri.parse("https://api.example.com"));
}
}
Here, ApiService directly depends on a concrete implementation (HttpClient).
✅ Good:
abstract class HttpService {
Future<void> get(String url);
}
class HttpClientService implements HttpService {
@override
Future<void> get(String url) async {
// actual request
}
}
class ApiService {
final HttpService httpService;
ApiService(this.httpService);
void fetchData() {
httpService.get("https://api.example.com");
}
}
Now ApiService depends on an abstraction, making it easy to mock or replace in testing.
💡 Applying SOLID in Flutter Apps
Here’s how these principles come together in Flutter:
| Layer | Responsibility | SOLID in action |
|---|---|---|
| UI / Widgets | Display data and handle user input | SRP: don’t put logic here |
| Bloc / ViewModel | State management | DIP: depend on abstract services |
| Repository | Data access | OCP: add new data sources without breaking |
| Services | Business logic (network, notifications, etc.) | SRP + LSP |
| Models / Entities | Represent domain objects | SRP |
Using Clean Architecture or DDD (Domain Driven Design) naturally enforces SOLID principles in Flutter.