Princípios SOLID no Flutter: Implementação Prática com Exemplos Reais

Introdução

Os princípios SOLID são um conjunto de boas práticas de programação que tornam o código mais organizado, flexível e fácil de manter. No Flutter, aplicá-los corretamente pode melhorar a arquitetura do projeto e facilitar a escalabilidade.

Neste artigo, vamos explorar cada um dos cinco princípios de SOLID com exemplos práticos em Dart/Flutter.

1. Single Responsibility Principle (SRP)

O princípio da responsabilidade única estabelece que cada classe deve ter um único propósito. No Flutter, isso significa separar lógica de negócios da UI.

Exemplo: Separando a Lógica de Requisição de API

Em um app que busca dados da API, ao invés de colocar a requisição diretamente na UI, criamos um serviço dedicado.


class UserService {
  final Dio dio;

  UserService(this.dio);

  Future<List> fetchUsers() async {
    final response = await dio.get('https://jsonplaceholder.typicode.com/users');
    return (response.data as List).map((e) => User.fromJson(e)).toList();
  }
}

A UI agora apenas exibe os dados:


class UserScreen extends StatelessWidget {
  final UserService userService;

  UserScreen({required this.userService});

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<List>(
      future: userService.fetchUsers(),
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.waiting) {
          return CircularProgressIndicator();
        }
        return ListView.builder(
          itemCount: snapshot.data?.length ?? 0,
          itemBuilder: (context, index) => ListTile(
            title: Text(snapshot.data![index].name),
          ),
        );
      },
    );
  }
}

2. Open/Closed Principle (OCP)

O código deve estar aberto para extensão, mas fechado para modificação. Isso significa que devemos usar abstrações para adicionar funcionalidades sem alterar código existente.

Exemplo: Criando um Sistema de Pagamento Flexível


abstract class PaymentProcessor {
  void pay(double amount);
}

class CreditCardPayment implements PaymentProcessor {
  @override
  void pay(double amount) {
    print('Pagando \$amount com cartão de crédito');
  }
}

class PaypalPayment implements PaymentProcessor {
  @override
  void pay(double amount) {
    print('Pagando \$amount com PayPal');
  }
}

class PaymentService {
  final PaymentProcessor processor;

  PaymentService(this.processor);

  void makePayment(double amount) {
    processor.pay(amount);
  }
}

Agora podemos adicionar novos métodos de pagamento sem modificar as classes existentes!

3. Liskov Substitution Principle (LSP)

O princípio de substituição de Liskov diz que subclasses devem ser substituíveis pelas superclasses sem alterar o comportamento do programa.

Exemplo: Criando Modelos de Notificação


abstract class NotificationSender {
  void send(String message);
}

class EmailNotification implements NotificationSender {
  @override
  void send(String message) {
    print('Enviando email: \$message');
  }
}

class SMSNotification implements NotificationSender {
  @override
  void send(String message) {
    print('Enviando SMS: \$message');
  }
}

void notifyUser(NotificationSender sender, String message) {
  sender.send(message);
}

void main() {
  notifyUser(EmailNotification(), 'Bem-vindo ao Flutter!');
  notifyUser(SMSNotification(), 'Seu código de verificação é 1234.');
}

4. Interface Segregation Principle (ISP)

O ISP afirma que uma classe não deve ser forçada a implementar métodos que não usa.

Exemplo: Interfaces para Diferentes Tipos de Relatórios


abstract class Report {
  void generate();
}

abstract class PrintableReport {
  void printReport();
}

class PDFReport implements Report, PrintableReport {
  @override
  void generate() {
    print('Gerando PDF');
  }

  @override
  void printReport() {
    print('Imprimindo PDF');
  }
}

class EmailReport implements Report {
  @override
  void generate() {
    print('Gerando relatório para e-mail');
  }
}

Agora cada classe implementa apenas os métodos necessários.

5. Dependency Inversion Principle (DIP)

O DIP sugere que módulos de alto nível não devem depender de módulos de baixo nível diretamente, mas sim de abstrações.

Exemplo: Injeção de Dependência para Banco de Dados


abstract class Database {
  void save(String data);
}

class FirebaseDatabase implements Database {
  @override
  void save(String data) {
    print('Salvando no Firebase: \$data');
  }
}

class SQLDatabase implements Database {
  @override
  void save(String data) {
    print('Salvando no SQL: \$data');
  }
}

class UserRepository {
  final Database database;

  UserRepository(this.database);

  void saveUser(String user) {
    database.save(user);
  }
}

Agora podemos trocar o banco de dados sem modificar o código do repositório!

Conclusão

Os princípios SOLID ajudam a criar um código mais estruturado, reutilizável e fácil de manter. No Flutter, aplicá-los corretamente pode evitar problemas no crescimento do projeto.

Publicar comentário