Pular para conteúdo

Aula 17 - Gerenciamento de Estado Avançado com BLoC e Provider 🎯

Objetivo Pedagógico

Objetivo: Dominar a arquitetura reativa em Flutter utilizando o padrão BLoC (Business Logic Component) com Streams de Dart, separando integralmente a lógica de negócios da camada de apresentação de widgets.


📑 1. Fundamentos Teóricos & Análise Técnica

O Flutter renderiza sua própria árvore de renderização gráfica diretamente sobre sua engine C++ (Impeller/Skia). No entanto, o uso ingênuo de chamadas a setState() em árvores de widgets profundas reconstrói a subárvore inteira, gerando queda de taxa de quadros e desorganização da lógica de controle.

O padrão BLoC (Business Logic Component) foi concebido pelo Google para resolver esse desafio: 1. O Mecanismo Baseado em Streams: - A comunicação entre os widgets e a lógica de negócios ocorre exclusivamente por meio de fluxos contínuos de dados (Streams e StreamControllers do Dart). - Events (Entradas): Ações emitidas pela interface (ex: AdicionarItemAoCarrinho, CarregarPerfil). - States (Saídas): Estados imutáveis emitidos pelo BLoC que informam à interface o que renderizar. 2. Equitable e Otimização de Reconstrução: - A biblioteca equatable implementa comparação por valor em vez de referência de memória. - O widget BlocBuilder utiliza a propriedade buildWhen(previous, current) para avaliar se o estado mudou de fato, evitando reconstruções idênticas na tela. 3. Injeção de BLoCs na Árvore de Widgets: - Utilização do BlocProvider para disponibilizar instâncias de BLoCs em escopos específicos da árvore de widgets via InheritedWidget, garantindo o fechamento automático (Dispose) das Streams quando a rota de navegação é desmontada.

📐 Arquitetura Conceitual & Diagrama de Fluxo

graph LR
    Widget["UI: Flutter Widgets"] -->|Despacha Evento (add)| Bloc["BLoC Component (StreamTransformer)"]
    Bloc -->|Executa Regra / Chama Repo| Repo["Data Repository"]
    Repo -->|Retorna Dados| Bloc
    Bloc -->|Emite Novo State (emit)| Stream["Stream de Estados Imutáveis"]
    Stream -->|BlocBuilder / BlocConsumer| Widget
    style Widget fill:#e1f5fe,stroke:#01579b
    style Bloc fill:#fff3e0,stroke:#e65100
    style Repo fill:#f3e5f5,stroke:#7b1fa2
    style Stream fill:#e8f5e9,stroke:#2e7d32

🔍 Pilares e Diretrizes Técnicas

Nesta unidade, aprofundamos os seguintes conceitos fundamentais: - Separação Estrita de Responsabilidades: Nenhum widget de interface deve conter código de acesso a dados ou regras matemáticas de negócio. - Imutabilidade de Estados com Equatable: Previne rebuilds desnecessários comparando o conteúdo das propriedades dos estados emitidos. - Tratamento de Concorrência com Transformers: Uso de operadores como restartable() ou droppable() para evitar requisições redundantes de busca enquanto o usuário digita. - Gerenciamento do Ciclo de Vida do Stream: Fechamento obrigatório dos controladores de fluxo para prevenir vazamentos de memória na VM do Dart.


🛠️ 2. Implementação Prática em Flutter Multiplataforma, Dart Streams e BLoC Pattern

Abaixo está a implementação técnica de referência, estruturada com padrões de engenharia de software e foco em robustez:

// cart_bloc.dart (BLoC Reativo com Eventos, Estados e BlocBuilder)
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:equatable/equatable.dart';

// 1. Definição de Eventos
abstract class CartEvent extends Equatable {
  const CartEvent();
  @override
  List<Object?> get props => [];
}

class AddProductEvent extends CartEvent {
  final String productId;
  const AddProductEvent(this.productId);
  @override
  List<Object?> get props => [productId];
}

// 2. Definição de Estados Imutáveis
abstract class CartState extends Equatable {
  const CartState();
  @override
  List<Object?> get props => [];
}

class CartInitial extends CartState {}

class CartLoadedState extends CartState {
  final List<String> items;
  final double totalPrice;
  const CartLoadedState({required this.items, required this.totalPrice});
  @override
  List<Object?> get props => [items, totalPrice];
}

// 3. Implementação do BLoC
class CartBloc extends Bloc<CartEvent, CartState> {
  CartBloc() : super(CartInitial()) {
    on<AddProductEvent>((event, emit) async {
      final currentItems = state is CartLoadedState 
          ? List<String>.from((state as CartLoadedState).items) 
          : <String>[];

      currentItems.add(event.productId);
      final newPrice = currentItems.length * 49.90;

      emit(CartLoadedState(items: currentItems, totalPrice: newPrice));
    });
  }
}

💡 Análise Passo a Passo do Código

  1. Uso de Equatable: Permite ao Flutter BLoC deduzir se os itens do carrinho realmente mudaram antes de acionar o redesenho dos widgets.
  2. Manipulação Segura com emit: Garante despacho sequencial de novos estados através do pipeline interno de corrotinas do Dart.
  3. Tipagem Rígida em Eventos: Fornece segurança contra parâmetros nulos ou eventos desconhecidos em tempo de compilação.

🎯 3. Próximos Passos & Sequência Didática