Introduction
From the past few articles, I have been writing about design patterns and had promised to continue the series by focusing on their uses in Flutter development. However, before we dive deeper into new design patterns, I believe it is crucial to understand the SOLID principles. Even if we think we already know these principles, there is always room for a more profound understanding. Going forward in this article, we will shift our attention to the SOLID principles. Without further delay, let’s deep dive into this.
What are SOLID Principles?
The SOLID principles were introduced by Robert C. Martin (a.k.a Uncle Bob) in his paper Design Principles and Design Patterns. These principles were intended to make software designs more understandable, flexible, and maintainable. The acronym SOLID stands for:
- S: Single Responsibility Principle (SRP)
- O: Open-Closed Principle (OCP)
- L: Liskov Substitution Principle (LSP)
- I: Interface Segregation Principle (ISP)
- D: Dependency Inversion Principle (DIP)
These principles provide a way to decouple software (reduce dependencies between software components) and make it more modular, flexible, and adaptable to changes. They are widely used in object-oriented programming but can also be applied to other paradigms.
Single Responsibility Principle (SRP)
According to the Single Responsibility Principle (SRP), a class should only have one reason to change. Let's understand this principle better with an example.
Problem
class User {
String name;
String email;
User(this.name, this.email);
void changeEmail(String newEmail) {
// Validate the email
if (validateEmail(newEmail)) {
this.email = newEmail;
} else {
print('Invalid email');
}
}
bool validateEmail(String email) {
// Check if the email is valid
return email.contains('@');
}
}
In the above code, the User class violates the Single Responsibility Principle(SRP) as it has two responsibilities: managing the user's data and validating the email.
Solution
class User {
String name;
String email;
User(this.name, this.email);
void changeEmail(String newEmail) {
if (EmailValidator.validate(newEmail)) {
this.email = newEmail;
} else {
print('Invalid email');
}
}
}
class EmailValidator {
static bool validate(String email) {
// Check if the email is valid
return email.contains('@');
}
}
Now, the User class is only responsible for managing the user's data, and the EmailValidator class is only responsible for validating emails. This adheres to the Single Responsibility Principle(SRP).
Open-Closed Principle (OCP)
According to the Open-Closed Principle (OCP), software entities (classes, modules, functions, and so on) should be open for extension but closed for modification. Let's understand this principle better with an example.
Problem
class Greeter {
void greet(String language) {
switch (language) {
case 'English':
print('Hello!');
break;
case 'Spanish':
print('Hola!');
break;
default:
print('Language not supported');
}
}
}
Now, suppose I want to add a new language, I have to modify the greet() method, which will violate the Open-Closed Principle(OCP).
Solution
We can solve this by creating separate classes for each language that implement a common Greeter interface.
abstract class Greeter {
void greet();
}
class EnglishGreeter implements Greeter {
@override
void greet() {
print('Hello!');
}
}
class SpanishGreeter implements Greeter {
@override
void greet() {
print('Hola!');
}
}
class FrenchGreeter implements Greeter {
@override
void greet() {
print('Bonjour!');
}
}
class HindiGreeter implements Greeter {
@override
void greet() {
print("Namaste!");
}
}
Now, the Greeter interface is open for extension (we can add new languages by creating new classes) but closed for modification (we don't have to modify the Greeter interface or any of the existing classes). This adheres to the Open-Closed Principle.
Liskov Substitution Principle (LSP)
According to the Liskov Substitution Principle (LSP), any instance of a derived (child) class should be substitutable for an instance of its base (parent) class without affecting the correctness of the program. Let's understand this principle better with an example.

Join the conversation! Your thoughts help the community grow.