SOLID principles in Dart(Flutter)
Tags: flutter, beginners, coding, programming
Published: 2023-07-23 16:50:46

The SOLID principles are a set of principles for software development aimed at making software more maintainable, scalable, and flexible. In this article, we will discuss how to apply the SOLID principles in Flutter with code examples.

**S - Single Responsibility Principle (SRP)**

The Single Responsibility Principle (SRP) states that a class should have only one reason to change. In other words, a class should have only one responsibility. A class that has multiple responsibilities is hard to maintain and modify.

Here is an example of a class that violates the SRP:

```dart
class User {
  int id;
  String name;
  
  void save() {
    // save user to database
  }
  
  void sendEmail() {
    // send email to user
  }
}

```

This class violates the SRP because it has two responsibilities: saving the user to the database and sending an email to the user. A better approach would be to split the class into two separate classes:


```dart
class User {
  int id;
  String name;
}

class UserRepository {
  void save(User user) {
    // save user to database
  }
}

class EmailService {
  void sendEmail(User user) {
    // send email to user
  }
}
```


**O - Open/Closed Principle (OCP)**

The Open/Closed Principle (OCP) states that a class should be open for extension but closed for modification. In other words, you should be able to extend a class's behavior without modifying its source code.

Here is an example of a class that violates the OCP:


```dart
class Rectangle {
  double width;
  double height;
  
  double area() {
    return width * height;
  }
}
```


If we want to create a new class for calculating the area of a circle, we would have to modify the `Rectangle` class which is currently violating the OCP. A better approach would be to create an interface for calculating the area and have each shape implement the interface:


```dart
abstract class Shape {
  double area();
}

class Rectangle implements Shape {
  double width;
  double height;
  
  double area() {
    return width * height;
  }
}

class Circle implements Shape {
  double radius;
  
  double area() {
    return pi * pow(radius, 2);
  }
}
```


**L - Liskov Substitution Principle (LSP)**

The Liskov Substitution Principle (LSP) states that objects of a superclass should be able to be replaced with objects of a subclass without affecting the correctness of the program. In other words, a subclass should be able to replace its superclass without breaking the code.

Here is an example of a class that violates the LSP:


```dart
class Rectangle {
  double width;
  double height;
  
  double area() {
    return width * height;
  }
}

class Square extends Rectangle {
  double side;
  
  @override
  double set width(double value) => side = value;

  @override
  double set height(double value) => side = value;
}

```

This class violates the LSP because a `Square` cannot be used in place of a `Rectangle` without affecting the correctness of the program. A better approach would be to create a separate class for `Square`:

```dart
abstract class Shape {
  double area();
}

class Rectangle implements Shape {
  double width;
  double height;
  
  double area() {
    return width * height;
  }
}

class Square implements Shape {
  double side;
  
  double area() {
    return pow(side, 2);
  }
}
```

## I - Interface Segregation Principle (ISP)

The Interface Segregation Principle (ISP) states that a class should not be forced to implement interfaces it does not use. In other words, a class should only depend on the interfaces it needs.

Here is an example of a class that violates the ISP:

```dart
abstract class Shape {
  double area();
  double perimeter();
}

class Rectangle implements Shape {
  double width;
  double height;
  
  double area() {
    return width * height;
  }
  
  double perimeter() {
    return 2 * (width + height);
  }
}

class Circle implements Shape {
  double radius;
  
  double area() {
    return pi * pow(radius, 2);
  }
  
  double perimeter() {
    return 2 * pi * radius;
  }
}
```

This class violates the ISP because a `Circle` does not have a perimeter. A better approach would be to create separate interfaces for area and perimeter:

```dart
abstract class Area {
  double area();
}

abstract class Perimeter {
  double perimeter();
}

class Rectangle implements Area, Perimeter {
  double width;
  double height;
  
  double area() {
    return width * height;
  }
  
  double perimeter() {
    return 2 * (width + height);
  }
}

class Circle implements Areaand {
  double radius;
  
  double area() {
    return pi * pow(radius, 2);
  }
}
```

## D - Dependency Inversion Principle (DIP)

The Dependency Inversion Principle (DIP) states that high-level modules should not depend on low-level modules. Both should depend on abstractions. In other words, you should depend on abstractions, not on concrete implementations.

Here is an example of a class that violates the DIP:

```dart
class UserRepository {
  void save(User user) {
    // save user to database
  }
}

class UserService {
  UserRepository userRepository;
  
  UserService(this.userRepository);
  
  void saveUser(User user) {
    userRepository.save(user);
  }
}
```

This class violates the DIP because `UserService` depends on the concrete implementation of `UserRepository`. A better approach would be to depend on an abstraction:

```dart
abstract class UserRepository {
  void save(User user);
}

class FirebaseUserRepository implements UserRepository {
  void save(User user) {
    // save user to Firebase
  }
}

class UserService {
  UserRepository userRepository;
  
  UserService(this.userRepository);
  
  void saveUser(User user) {
    userRepository.save(user);
  }
}
```

By depending on an abstraction, `UserService` is no longer tied to a specific implementation of `UserRepository`, making it more flexible and maintainable.

## Conclusion

The SOLID principles are important for building maintainable, scalable, and flexible software. By following these principles in your Flutter code, you can create code that is easier to maintain and modify. Remember, the SOLID principles are not rules, but rather guidelines that can help you write better code.