Ao trabalhar em um aplicativo Flutter, você pode encontrar a necessidade de dividir um grande componente de IU em vários menores para melhorar a legibilidade do código. Com vários componentes, é crucial implementar uma comunicação eficaz entre eles. Todos os componentes da IU devem estar cientes do estado do aplicativo o tempo todo. Isso é chamado de gerenciamento de estado.

No Flutter, você pode gerenciar o estado do seu aplicativo apenas usando setState . Mas, embora setState possa ser seu melhor amigo, não é uma boa ideia depender apenas dele. Há muitos outros fatores que você também deve considerar ao desenvolver um aplicativo Flutter, como arquitetura, escalabilidade, legibilidade, complexidade, etc. Ficar em cima de tudo requer uma técnica de gerenciamento de estado eficaz.

Existem inúmeras soluções de gerenciamento de estado disponível para Flutter, incluindo Provider, InheritedWidget e InheritedModel, Redux, BLoC, GetIt, MobX, Riverpod, etc. Neste tutorial, vamos nos concentrar no uso de padrão de design BLoC para gerenciamento de estado no Flutter. Explicaremos o que significa BLoC e demonstraremos como implementar qualquer recurso no BLoC.

O que é BLoC?

Os componentes de lógica de negócios (BLoC) permitem que você separe a lógica de negócios da IU. Escrever código em BLoC torna mais fácil escrever e reutilizar testes.

Em termos simples, o BLoC aceita um fluxo de eventos, processa os dados com base em eventos e produz a saída como estados. Veja o exemplo simples abaixo:

Processar eventos e produtos Estados

Assim que o botão Girar 90 ° é clicado, o RotateEvent é despachado para BLoC e o estado que representa a rotação, ou seja, RotatedState , é emitido. O widget de triângulo gira ao receber o RotatedState do BLoC. Da mesma forma, o widget de círculo muda de cor quando o botão Alterar cor para vermelho é clicado.

Uma vez que o BLoC lida com a operação de rotação e mudança de cor, ambas as operações podem ser realizadas em qualquer widget. Isso facilita a reutilização do código.

Conceitos BLoC importantes

Antes de começarmos, vamos revisar alguns conceitos e termos básicos do BLoC para que todos concordemos.

Eventos

Os eventos dizem ao BLoC para fazer algo. Um evento pode ser disparado de qualquer lugar, como de um widget de IU. Eventos externos, como mudanças na conectividade da rede, mudanças nas leituras do sensor, etc., são semelhantes a:

 class RotateEvent { ângulo duplo final; const RotateEvent (this.angle); @sobrepor Listar  obter adereços=> [ângulo];
}

BLoC

BLoC é um homem no meio. Toda a lógica de negócios fica dentro do arquivo BLoC. Ele simplesmente aceita eventos, executa a lógica e fornece os estados. É assim que parece:

 classe TransformationBloc estende Bloc  { TransformationBloc (): super (RotatedState (ângulo: 0); @sobrepor Stream  mapEventToState ( Evento TransformationEvent) async * { if (evento é RotateEvent) { rendimento RotatedState (ângulo: event.angle); } }
}

Estados

Os estados representam as informações a serem processadas por qualquer widget. Um widget muda a si mesmo com base no estado.

 class RotatedState { ângulo duplo final; const RotatedState ({@ obrigatório this.angle}); @sobrepor Listar  obter adereços=> [ângulo];
}

Cubit

Cubit é uma versão mais simples do padrão BLoC. Elimina a necessidade de escrever eventos.

O Cubit expõe funções diretas, que podem resultar em estados apropriados. Escrever um Cubit em vez de BLoC também reduz o código clichê, tornando o código mais fácil de ler.

Aqui está um exemplo simples:

 classe TransformCubit extends Cubit  { TransformCubit (): super (RotatedState (ângulo: 0)); void rotate (ângulo duplo) { emitir (RotatedState (ângulo: ângulo)); } }

Gerenciando estado em Flutter com setState (sem BLoC)

Antes de destacar os benefícios do gerenciamento de estado no Flutter com BLoC, vamos percorrer o processo de uso de setState para gerenciamento de estado.

Nosso aplicativo de exemplo Flutter mostrará uma lista de produtos disponíveis. Um produto pode ser adicionado ou removido do carrinho clicando no ícone ao lado do nome do produto. O número de itens no carrinho é atualizado de acordo:

Cart Updating

Com setState , toda a IU é dividida em três classes:

  1. home.dart é o arquivo principal que contém o scaffold e AppBar . AppBar contém o widget do ícone do carrinho
  2. product_list.dart mostra a lista de produtos
  3. product_tile.dart mostra o item individual do produto.

Esta é a aparência:

IU dividida em três classes

A lista de itens no carrinho é passada desde o widget Home (topo) até o widget ProductTile (parte inferior) para verificar se um determinado item sai de o carrinho ou não. Em caso afirmativo, o ícone do carrinho será destacado.

Ícone do carrinho realçado

Clicar no ícone do carrinho ao lado do nome do produto adiciona o item ao carrinho. O retorno de chamada para atualizar o ícone do carrinho no AppBar é feito de ProductTile (inferior) para Home (superior).

Callback para atualizar o carrinho

O problema com setState

A abordagem setState para gerenciamento de estado no Flutter funciona bem para aplicativos simples com apenas alguns componentes. Mas para aplicativos Flutter mais complexos do mundo real com árvores profundas de widgets, o uso de setState pode levar aos seguintes problemas:

  • Duplicação de código-os dados devem ser passados ​​de todos os widgets para o widget inferior, o que torna o código difícil de ler
  • Degradação de desempenho devido a redesenhos desnecessários que resultam da elevação de um setState para um widget pai com uma hierarquia profunda

Como gerenciar o estado no Flutter com BLoC

Agora vamos implementar o mesmo recurso com BLoC.

Primeiro, adicione a biblioteca BLoC :

dependências

: vibração: sdk: flutter cupertino_icons: ^ 1.0.2 flutter_bloc: ^ 7.0.0

A seguir, crie e adicione um observador BLoC. Isso ajuda a determinar a sequência de eventos e estados que ocorreram, o que é ótimo para depurar o aplicativo.

 void main () { Bloc.observer=SimpleBlocObserver (); runApp (MyApp ());
}
import'package: flutter_bloc/flutter_bloc.dart'; ///Custom [BlocObserver] que observa todas as instâncias de bloco e cúbito.
class SimpleBlocObserver extends BlocObserver { @sobrepor void onEvent (bloco bloco, evento de objeto) { super.onEvent (bloco, evento); imprimir (evento); } @sobrepor void onTransition (bloco bloco, transição de transição) { super.onTransition (bloco, transição); imprimir (transição); } @sobrepor void onError (BlocBase bloc, Object error, StackTrace stackTrace) { imprimir (erro); super.onError (bloco, erro, stackTrace); }
}

Crie eventos para adicionar e remover produtos da lista de itens do carrinho:

 import'package: equatable/equatable.dart'; classe abstrata CartEvent extends Equatable { const CartEvent (); @sobrepor Listar  obter props=> [];
} class AddProduct extends CartEvent { final int productIndex; const AddProduct (this.productIndex); @sobrepor Listar  obter props=> [productIndex]; @sobrepor String toString ()=>'AddProduct {index: $ productIndex}';
}

Agora, crie estados para representar um produto sendo adicionado e removido:

 import'package: flutter/material.dart'; classe abstrata CartState { Lista final  cartItem; const CartState ({@ obrigatório this.cartItem}); @sobrepor Listar  obter props=> [];
} classe ProductAdded extends CartState { Lista final  cartItem; const ProductAdded ({@ required this.cartItem}): super (cartItem: cartItem); @sobrepor Listar  obter props=> [cartItem]; @sobrepor String toString ()=>'ProductAdded {todos: $ cartItem}';
}

Escreva a lógica de negócios para adicionar e remover produtos nos cartItems e emitir o respectivo estado. A lista real de itens no carrinho é mantida no nível BLoC.

 class CartBloc extends Bloc  { CartBloc (): super (ProductAdded (cartItem: [])); Lista final  _cartItems=[]; Listar  obter itens=> _cartItems; @sobrepor Stream  mapEventToState (evento CartEvent) async * { if (event is AddProduct) { _cartItems.add (event.productIndex); yield ProductAdded (cartItem: _cartItems); } else if (event is RemoveProduct) { _cartItems.remove (event.productIndex); yield ProductRemoved (cartItem: _cartItems); } }
}

Em seguida, envolva o widget de cadafalso dentro de BlocProvider .

BlocProvider é um widget Flutter que disponibiliza qualquer BLoC para toda a árvore de widgets abaixo dele. Em nosso caso, qualquer widget entre Home (superior) e ProductTile (inferior) pode ter acesso ao carrinho, portanto, não há necessidade de passar os dados do carrinho de cima a árvore de widgets na parte inferior.

 BlocProvider ( criar: (_)=> CartBloc (), criança: Scaffold ( appBar: CartCounter (), corpo: ProductList (), ));

Envolva o ícone do carrinho e a lista de produtos dentro do BlocBuilder . O BlocBuilder simplesmente reconstrói o widget dentro dele ao receber os novos estados do BLoC.

//Ícone do carrinho
BlocBuilder  (construtor: (_, cartState) { Lista  cartItem=cartState.cartItem; retornar Posicionada ( esquerda: 30, criança: Container ( preenchimento: EdgeInsets.all (5), decoração: BoxDecoration ( borderRadius: BorderRadius.circular (10), cor: Colors.red), filho: Texto ( '$ {cartItem.length}', style: TextStyle (fontWeight: FontWeight.bold), ), ), );
}),
//Lista de produtos BlocBuilder  (construtor: (_, cartState) { Lista  cart=cartState.cartItem; return LayoutBuilder (construtor: (contexto, restrições) { return GridView.builder ( itemCount: 100, itemBuilder: (contexto, índice)=> ProductTile ( itemNo: index, carrinho: carrinho, ), gridDelegate: SliverGridDelegateWithFixedCrossAxisCount ( crossAxisCount: constraints.maxWidth> 700? 4: 1, childAspectRatio: 5, ), ); });
});

Nota: O BlocBuilder para CartBloc é adicionado apenas em dois lugares porque queremos que esses dois widgets sejam reconstruídos quando algo acontecer em CartBloc . Essa abordagem de atualizar apenas os widgets necessários reduz significativamente o número de redesenhos desnecessários.

A próxima etapa é disparar eventos para CartBloc para adicionar e remover itens do carrinho. BlocProvider.of (contexto) encontra a instância mais próxima de CartBloc na árvore do widget e adiciona os eventos a ela:

 IconButton ( chave: Chave ('icon_ $ itemNo'), ícone: cart.contains (itemNo) ? Ícone (Icons.shopping_cart) : Ícone (Icons.shopping_cart_outlined), onPressed: () { ! cart.contains (itemNo) ? BlocProvider.of  (contexto).add (AddProduct (itemNo)) : BlocProvider.of  (contexto).add (RemoveProduct (itemNo)); },
)

Agora substitua BlocBuilder por BlocConsumer . BlocConsumer nos permite reconstruir o widget e reagir aos estados. Deve ser usado apenas quando você deseja reconstruir o widget e também realizar alguma ação.

Para nosso exemplo, queremos atualizar a lista e mostrar uma lanchonete sempre que um produto for adicionado ou removido do carrinho:

 BlocConsumer  (
ouvinte: (contexto, estado) { Scaffold.of (context).showSnackBar ( Lanchonete( Conteúdo do Texto( o estado é ProductAdded?'Adicionado ao carrinho.':'Removido do carrinho.'), duração: Duração (segundos: 1), ), );
},
construtor: (_, cartState) { Lista  cart=cartState.cartItem; return LayoutBuilder (construtor: (contexto, restrições) { return GridView.builder (); });
});

Opcionalmente, se você deseja reduzir algum código clichê e a sequência dos estados não importa para você, experimente o Cubit. Esta é a aparência de CartCubit :

 class CartCubit estende Cubit  { CartCubit (): super (ProductAdded (cartItem: [])); Lista final  _cartItems=[]; Listar  obter itens=> _cartItems; void add (int productIndex) { _cartItems.add (productIndex); emit (ProductAdded (cartItem: _cartItems)); } void remove (int productIndex) { _cartItems.remove (productIndex); emit (ProductRemoved (cartItem: _cartItems)); }
}

Observação: substitua CartBloc por CartCubit em todo o código e dispare os eventos conforme mostrado abaixo:

 onPressionado: () { ! cart.contains (itemNo) ? BlocProvider.of  (contexto).add (itemNo) : BlocProvider.of  (contexto).remove (itemNo);
},

O resultado é o mesmo, mas com gerenciamento de estado aprimorado:

Cart Updating

Conclusão

Ter uma arquitetura BLoC sólida em vigor leva a uma boa separação de interesses. Embora o uso do padrão BLoC exija mais código do que o uso de setState , ele torna o código mais legível, escalonável e testável.

Neste tutorial, cobrimos os fundamentos do uso do padrão BLoC no Flutter e percorremos um exemplo prático para destacar os benefícios do uso do BLoC para gerenciamento de estado no Flutter sobre a abordagem setState .

Você pode encontrar o código-fonte completo para este exemplo em GitHub .

A postagem Gerenciamento de estado no Flutter usando o padrão de design BLoC apareceu primeiro no LogRocket Blog .

Source link