Arquitetura BLoC — Porque tão importante utilizar?

Fellipe Malta
EngApp
Published in
7 min readDec 9, 2018

Por que essa arquitetura é tão importante e por que você precisa aprender mais sobre ela?

Dificuldade: intermediária.

Bom, antes de mais nada, vamos partir do ínicio. Essa arquitetura foi criada por Paolo Soares e Cong Hui, ambos da Google e apresentada pela primeira vez na DartConf de 2018 (Janeiro 23–24, 2018). Veja o vídeo completo.

Para se ter domínio completo dessa arquitetura, antes de tudo, é preciso conhecer Streams, fazer uso delas e entender o conceito por trás de programação reativa.

Streams

Simplificando, é um cano, com uma entrada(sink) e uma saída(stream). Entenda que por entrada e saída, em termos de código, eu quero dizer que a função que o onPressed de botão vai executar, é o sink, pois, estarei adicionando algo nesse cano. Enquanto que a saída, stream, pode ser um texto que irei exibir ou ficar alterando ele.

Aqui vai um exemplo bem prático:

Percebam que no onPressed do FAB, eu estou alterando o valor da variavel _counter sempre que eu o pressionar. Enquanto que meu StreamBuilder recebe a saída (_counterController.stream) e renderizo o novo valor no widget de texto que é o snapshot.data.

Linha 4. Eu inicializo meu controlador do tipo <int> para que o mesmo não seja de algum tipo dinâmico.

Linha 8. Minha função dispose() recebendo o fechamento do StreamController que eu criei para que eu possa liberá-lo quando não estiver mais fazendo uso. Evitando assim, perda de performance. É obrigatório que você faça isso.

Linha 25. Adiciono a saída do meu cano, ou seja, o que eu quero que apareça na tela do meu dispositivo. Nesse caso, é a saída do valor inteiro, que irá ficar se alterando conforme eu clique no FAB.

Linha 26. Inicio com um valor qualquer, poderia ser 10, 20, 30, fica a sua escolha.

Linhas 27~29. Crio o builder que vai receber o contexto desse arquivo e crio o snapshot que serve para que eu possa exibir o conteúdo que a stream me fornece.

Linha 38. Crio a entrada que vai adicionar valores no cano. Como o exemplo é bem simples, podem ver que eu estou alterando sempre o valor da minha varíavel _counter.

Pronto, esse é o exemplo básico de como você pode transformar sua aplicação que faz uso do setState(), o qual não é útil quando você tem uma aplicação grande e nós vamos ver o por que disso logo mais, para uma aplicação que faz uso de streams.

setState() vs Streams

Agora você deve estar se perguntando: “Por que esse cara não usou o setState pra fazer isso? Por que eu deveria fazer isso com streams?” A resposta pra essas duas perguntas é bem simples: Perfomance e eficiência.

Detalhes, detalhes e detalhes…

Não estou dizendo que usar setState() para desenvolver suas aplicações seja algo ruim, ou que uso de streams seja a melhor opção, mas, tem um porém, e esse porém vem do tamanho da sua aplicação, e eu não falo em relação a MB, falo com relação a complexidade.

Abstraia comigo, você tem sua UI montada, alterando alguns widgets com uso de setState(), até aí sem problemas né? Bom, caso você seja leigo ou não tenha parado para estudar a diferença e por isso chegou até aqui, eu vou lhe dizer por que o uso do setState() para uma aplicação média/grande não é eficiente/útil.

A cada alteração que você faz em um widget usando o setState(), você não renderiza apenas aquele widget, você renderiza TODA sua UI, de novo, ou seja, para uma aplicação complexa, que possui varias animações, vários widgets compostos, outros exibindo informações de uma API externa, é uma queda de performance grande e que não pode ser permitida a partir de certo patamar.

Agora, claro, se você pretende criar seu MVP para exibir para um cliente ou só para ter mais noção e aprendizado, é útil sim e bastante. Porém, a partir do ponto que sua aplicação começa a se tornar complexa, uma ótima saída é partir para programação reativa e caso venha crescer ainda mais, aprender e aplicar a arquitetura BLoC.

Então, o uso de Streams é o mais recomendado?

Sim e não. Como foi dito no último parágrafo, isso depende, mas claro, eu tenho certeza que você programador, que está lendo este artigo, não quer ficar pra sempre fazendo a mesma coisa e esperando algum resultado diferente? Certo? Isso é o conceito de insanidade. Tenho certeza que você quer subir no mercado e ter seu trabalho reconhecido e se destacar dentre os demais.

Respondendo com mais detalhes a pergunta desse tópico, o uso de Streams é o mais recomendado sempre, em minha opinião, por que? Deixa seu código mais limpo, mais legível e de fácil manutenção para futuros programadores ou inclusive até para você mesmo caso se depare com seu código futuramente.

Aprendi Streams e agora?

Então você aprendeu a fazer uso de Streams na sua aplicação e não sabe mais pra onde ir? Não sabe mais em qual direção seguir? Muito simples, agora você vai aprender como gerenciar o estado da sua aplicação usando a arquitetura BLoC.

Já imaginou toda sua UI separada da sua regra de negócio? Acredito que você já deve ter feitos códigos no qual tudo e absolutamente tudo estava no mesmo arquivo, sua UI, regra de negócio, requisições para uma API. Enfim, uma bagunça que você as vezes tem dificuldade pra encontrar o que precisa.

Com isso, o BLoC veio para ajudar vocês e parando para estudar e focar de verdade, verá que é bem simples de abstrair e aplicar ele no desenvolvimento do seu produto.

Vamos lá…

Vamos seguir o exemplo do contador e adicionar a arquitetura BLoC nele. Apesar de ser uma aplicação bem simples, o intuito deste artigo é com que você aprenda e possa abstrair com mais facilidade o conteúdo.

1 — Crie uma interface que irá implementar o BLoC

Essa interface pode se tornar padrão para seus futuros projetos.

2 — Vamos criar o BLoC para esse contador

Linha 4. Criei a classe a qual vai fazer uso da interface BlocBase que acabamos de criar.

Linhas 5~10. Primeiro passo oficial. Vamos iniciar nossa variável _counter e ela será inicializada com valor 0.

Logo depois, vamos criar nosso StreamController do tipo <int> porque não queremos deixar esse tipo como dinâmico.

Se lembra no início que Stream é um cano com entrada e saída? Ótimo, vamos adicionar na nossa entrada um Sink<int> que vai ser nosso get para que não tenhamos que instanciar mais da forma como instanciamos no exemplo de streams logo no começo.

Depois nós vamos criar nossa saída e referenciar através de um get.

Linhas 13~14. Nesse exemplo em si, que possui um botão que altera o valor de um widget, temos que cuidar dele também. Criamos então um controlador e uma entrada (sink) para o mesmo e referenciamos também com um get.

Percebe o padrão? Criar a(s) variável(eis) que irei usar no meu componente.

Crio o controlador, crio meu sink e stream e referencio fazendo uso de um get.

Até ai tudo certo né? Espero que tenha entendido, é bem simples isso que fizemos até agora.

Linha 16. Agora, temos que criar um construtor para inicializarmos a variável que estará contida no componente, mas que, ele não precisa saber e sim, nosso CounterBloc. Dentro, também passamos um listen que vai receber como parâmetro o dado do tipo do controlador (int) que você especificou para o _incrementController logo acima e que vai ficar ouvindo sempre a essas alterações.

Linha 20. A ordem dessas duas últimas funções pouco importa. O dispose você precisa criar por que nossa interface faz uso de um dispose, que serve, como já foi dito e será repetido agora: fechar o cano quando você não estiver mais fazendo uso dessa stream.

Linha 26. E lógico, eu preciso criar uma função que vai cuidar da alteração de valor do meu contador. Perceba que ela recebe um parâmetro (data) que é do mesmo tipo que o listen que tá ouvindo a saída (stream) do _incrementController. E sem antes esquecer de adicionar na entrada (sink) o que queremos enviar para nosso cano. Com isso, nós podemos alterar nossa variável _counter.

Já acabou?

Estamos quase lá, agora, precisamos alterar algumas coisas na nossa UI. Vamos voltar para ela:

Primeiramente, precisamos ir no componente o qual nossa BlocMainPage está contida e tornar ela filha do BlocProvider que criamos:

Perceba que com isso, eu consigo ter acesso, na BlocHomePage, do CounterBloc que criamos.

Em seguida, vamos modificar nossa BlocHomePage:

Perceba algumas diferenças do primeiro exemplo usando Streams para esse, usando a arquitetura BLoC.

1 — Não temos mais o controlador sendo instanciado.

2 — Não temos mais nossa função dispose para liberar a stream quando ela termina de ser usada.

A última e a mais importante, nosso componente é todo Stateless

Vamos lá, agora, o que precisamos para juntar tudo isso que fizemos e funcionar?

Linhas 2 e 3. Importar nossa interface BlocProvider e CounterBloc que criamos.

Linha 8. Criar dentro do widget nosso _counterBloc que vai receber o BlocProvider do tipo CounterBloc, com isso, vamos ter acesso aos nossos get que criamos na CounterBloc.

Linha 21. Agora passamos nossa stream de saída com o outCounter que também criamos na CounterBloc. Percebe que coisa linda tá ficando? ❤

Linha 35. Chame a função que vai executar o incremento da variável _counter passando null como parâmetro. Por que null? Por que já temos um parâmetro dentro dessa função lá no CounterBloc, então, não faz sentido já que ela já recebe um argumento quando a criamos no CounterBloc.

Conclusão

Aplicar a arquitetura não é tão díficil, díficil é encontrar gente que saiba passar o conteúdo de forma clara e explicativa e foi isso que tentei fazer aqui. Espero que tenha sido de grande ajuda para vocês, programador que deseja melhorar seu nível e tenha certeza que você aprendendo o lhe foi dito aqui, você só tem a crescer.

Achou o artigo complicado? Difícil de entender? Deixe de comentário, críticas construtivas são sempre bem vindas.

Código desse exemplo se encontra aqui.

Quer mais detalhes sobre a arquitetura? Clique aqui.

--

--