Alura > Cursos de Mobile > Cursos de Flutter > Conteúdos de Flutter > Primeiras aulas do curso Flutter: Streams de programação reativa com BLoC

Flutter: Streams de programação reativa com BLoC

Bloc e flutter bloc - Apresentação

Boas-vindas a mais um curso de Flutter. Nesse curso, nós vamos utilizar o Flutter “Bloc”, que é baseado no pacote “Bloc”, cuja a ideia para nós é aprender a utilização do padrão Bloc, mais especificamente de um Cubit, criação dos eventos, criação dos estados, gerenciamento dos estados e lógica de negócios síncrona ou assíncrona que é executada de acordo com a mudança de estado, em função da mudança de estado ou gerando mudanças de estado.

Tudo isso combinando com a nossa interface e com o usuário. Para executar esse projeto desse curso, nós vamos utilizar um projeto que já foi criado em cursos anteriores de Web API com Flutter. Se você já fez os cursos anteriores de pré-requisitos, podemos continuar o utilizando.

Se você não fez, faça os outros cursos ou você vai, durante esse curso, baixar a situação anterior e continuar a partir de lá.

Então, nós vamos escrever bastante código, refatorar bastante código, alterar bastante da funcionalidade para adicionar vários padrões em algumas dessas telas, evitando ser muito repetitivo para que possamos fazer uma, duas vezes cada tipo de trabalho que vamos ter de comportamento síncrono ou assíncrono, e que você possa depois expandir para outras telas, outras funcionalidades, como você desejar dentro desse ou outros projetos.

O “Bloc” vai nos ajudar bastante no dia a dia a separar as responsabilidades de interface com o usuário, de estado, de mudança de estado e de lógica de negócios que está acontecendo no nosso cliente mobile. Vamos lá, vamos estudar.

Bloc e flutter bloc - Configurando o projeto e extração inicial

Para baixar o projeto que utilizaremos click aqui

Aviso: O projeto que está disponível no repositório usa uma versão anterior a Flutter 2+, portanto, se você estiver usando a versão mais atual do Flutter poderá ver alguns avisos (warnings) que não comprometem o fluxo e o entendimento do curso.

Para baixar o servidor click aqui

Então, vamos começar. Primeira coisa é, nós vamos continuar um projeto que já tinha sido trabalhado em um curso anterior, o “flutter-webapi-2”, a parte dois desse projeto.

Se você já fez esse projeto, maravilha, continua com ele. Se você não fez esse projeto, dá uma conferida se você tem todos os pré-requisitos daquele curso, se você tiver, pode clicar aqui em “Code > Download ZIP”. Nas atividades vai ter o link no “github.com/alura-cursos/flutter-webapi-2”.

Isso é o nosso front-end mobile. Na parte do back-end, do servidor, nós vamos utilizar o mesmo back-end do outro curso, que é o “bytebank-api”, que você pode entrar também no “github.com/alura-cursos/bytebank-api”, descer e encontrar aqui um “jar”, que na verdade é um “zip”. Você descompacta e tem um “jar” que é o nosso servidor. Nós também vamos utilizar.

Eu já baixei esses dois arquivos aqui, o “bytebank-api” e o “flutter-webapi-2”. Baixei esses dois e vou descompactar os dois. “Extract All > Extract” e esse daqui é o “bytebank-api-runnable”, onde nós temos o nosso servidor.

E vou extrair esse outro aqui, o “flutter-webapi-2”, “Extract All > Extract”, esse outro “zip” que é o “flutter-webapi-2”.

Colocou um dentro do outro, eu vou jogar esse daqui, o “flutter-webapi-2”, para fora, para não ficar um dentro do outro.

Vou jogar isso daqui tudo para cá, para “flutter-webapi-2-master”, para não ficar um diretório dentro do outro diretório com o mesmo nome. A maneira que eu descompactei foi meio burra, mas não tem problema, o importante é termos tudo descompactado aqui para nós.

Então, “bytebank-api-runnable” e o “flutter-webapi-2”. Mesma coisa no “bytebank-api-runnable”, eu fui não muito esperto e descompactei um diretório dentro do outro diretório. Os costumes de viver no Windows depois de tantos anos.

Então temos os dois diretórios aqui. Eu vou agora para o meu Android Studio. Lembrando, se você utiliza o Intellij com plugins, não tem problema, o importante é termos o Intellij, o Android Studio com as configurações, com os plugins que nós precisamos para o Flutter. Eu vou utilizar isso.

Se você utilizar o VS Code ou outras coisas, não tem problema. Claro que você não vai clicar nos mesmos botões que eu, mas a ideia do Dart e do Flutter será equivalente.

Eu vou clicar aqui em “Abrir um projeto” do Android Studio ou de Flutter, e vou procurar o diretório onde está este arquivo, “AndroidStudioProjects > flutter-webapi-2”. Ele já detectou que é um projeto aqui Android Studio.

Está abrindo aqui o projeto, “Android framework is detected”, eu vou clicar aqui no “X”. Talvez, se for a primeira vez que você estiver abrindo – não deveria ser, você já deveria ter feito outros cursos de Flutter para cá e neste daqui.

Mas a primeira vez que você está abrindo o Android Studio talvez você precise configurar o Flutter no “File > Settings”, e dentro de “Settings” você vai procurar em “Linguas”, “Languages & frameworks”, depois de ter instalado o plugin do Flutter, como fazemos lá no primeiro curso de Flutter nosso.

Em “Flutter”, talvez você tenha que configurar o seu “Flutter SDK path” para poder utilizar o SDK do Flutter para todos os seus projetos. Ou para esse projeto, ou para todos os seus projetos.

Eu coloquei aqui, ele está utilizando uma versão específica do Flutter, vou dar um “Ok” e ele deve fazer o build a partir de agora com esse SDK.

O que que eu vou fazer agora? Bom, vou fechar esse “README” que eu não estou utilizando, aqui eu tenho o meu projeto, eu tenho aqui o meu projeto lib, eu tenho um main.dart. vou executar. Para isso, eu vou no “AVD Manager”, vou startar o meu celular, o meu dispositivo. Na minha máquina, ele está falando que ele não localiza o ADB, mas tudo funciona, estou feliz e contente, vamos para frente.

Ele está reclamando aqui alguma coisa do build que ele não está conseguindo. Vou pedir para ele dar um get dependencies para pegar as dependências que precisam do Flutter para nós. Deve estar pegando agora as dependências e daqui um tempo, em algum tempo, esse “Dart Analysis” aqui deve rebuildar feliz. Ficou feliz.

Ele está ainda reclamando do não pegou ainda não rodou, eu vou mandar ignorar, porque já rodou, mas para mim eu estou com esse bug, vamos para frente.

Fechei, não queria fechar, o que eu quero agora é rodar. Então, no meu dispositivo – claro você pode estar com o seu celular conectado via rede e querer rodar no seu celular para ficar evitando utilizar um emulador caso seu computador não aguente um emulador que é pesado, eu entendo, não tem problema nenhum.

Eu preferia muito mais esse computador aqui que eu estou usando, que é com o Windows, é meu computador, entre aspas, novo, razoavelmente novo, que é justo para eu poder gravar cursos de Flutter, entre outras coisas com emuladores.

Vamos lá. “Run main.dart”, cliquei aqui, vou esperar, ele vai rodar para nós no nosso dispositivo aqui. Daqui a pouco, vamos ver, daqui a pouco ele roda. Enquanto isso, percebe, tem um import aqui que não está sendo utilizado, eu vou dar uma rapada nesse arquivo. Tirei aqui essa linha.

À medida que ele carrega o nosso aplicativo, eu vou entrar aqui, eu queria executar aquele “jar”, executar o servidor. Eu poderia abrir o projeto, eu poderia fazer de várias maneiras, eu vou abrir um “Terminal”, olha aqui, “Terminal”. Aqui no próprio Android Studio eu abro um “Terminal”, eu vou navegar – no meu caso aqui, é Windows – no meu bytebank-api-runnable.

Eu dou um clear, será que clear pega? clear pega também. ls, eu misturo as coisas, perdão, ls ou dir. Nós temos um server.jar, nós podemos dar um java -jar server.jar. Essa é a versão padrão do “bytebank server site”. Estou rodando o “bytebank server site”, maravilha, “Started”, “Flutter supports hot reload”, eu sei.

Está aqui com o “Bytebank” rodando e eu posso clicar “Transaction Feed”. Está carregando aqui as transações, depois de carregar as transações deveria trazer para mim as transações, mas demorou e não trouxe transação, tem algo estranho.

Lembra uma coisa que é importante, nós temos que vir aqui no nosso http, nos cursos anteriores, webclient.dart, estava hardcodado o IP da tua máquina. A minha máquina provavelmente não é “192168581”.

Então, o que que eu vou fazer? Eu vou abrir o meu “Terminal”, que eu já tinha “Terminal” ali, aliás, eu poderia abrir aqui, perdão, simplesmente o mais e dar, no meu caso do Windows, um ipconfig para descobrir o meu IP.

O meu IP é “192168586161”. O seu pode ser outra coisa, não tem problema, inclusive, nós podemos tentar acessar isso aqui no nosso navegador para ter certeza. Trouxe nenhuma transação.

Então, voltamos aqui agora no “Bytebank > Transaction Feed”, aqui o local, aqui eu voltei para cá, para o “Bytebank”, aqui ele está demorando, não trouxe nenhuma transação, “Transfer”. Faltou um hot reload aqui para nós. Vou dar aqui um hot reload.

“Transaction Feed”, não tem transação, “Transfer”, já trouxe aqui diversos nomes para mim, um monte de lixo que eu já estava trabalhando. Ele trouxe aqui diversos usuários para mim que eu tenho aqui no meu dispositivo agora armazenado.

E eu posso sair trabalhando, eu quero transferir para o Guilherme, eu quero transferir “1000”. Quando eu peço para transferir, ele pede para autenticar, lembra, ele precisa de uma senha. Então, se nós voltarmos lá para o nosso navegador, nós vamos ver lá na documentação do “bytebank-api” as propriedades, a senha padrão, cadê a senha padrão? “1000”. A senha padrão é “1000”.

Então, eu vou lá, senha padrão “1000”, “Confirmar”. “Sending”, transação com sucesso. Se nós voltarmos para o “Transactions Feed”, traz a transação de “1000” para a conta do “2222”.

Então, temos aqui o projeto configurado rodando. Só para nós fazermos mais uma mudança, agora que você já tem o projeto rodando, queria fazer só uma única mudança que é nós começarmos a limpar à medida que vamos desenvolver agora, porque queremos adicionar padrões de qualidade de código, padrões que facilitem o trabalho, que separem responsabilidades, então, eu já vou separar uma coisa daqui. O meu MaterialApp, esse tema aqui.

Esse tema arranca fora. Esse daqui é o tema do “Bytebank”, bytebankTheme. Então, eu já vou criar, aqui em “components", eu já vou criar um novo arquivo, “New > File”, chamado theme.dart – perdão pelo meu inglês. No theme.dart, eu vou criar uma variável final final bytebankTheme =, igual a isso tudo.

Fecha um ponto e vírgula aqui, ao final, vou dar um autocomplete, não é autocomplete, mas no meu caso é “Alt + Return”, depende o OS de você. “Import library”, um “Ctrl + Espaço” ou “Alt + Enter”, depende do seu sistema operacional etc. Lembra que você pode sempre ter aqui um “Code”, você pode ter no “Refactor”, você vai ter esses atalhos que nós estamos utilizando.

“Code > Completion”, “Code > Generation”, eu vou usar vários desses. Lembra de utilizar de acordo com o seu sistema operacional.

Então, fiz aqui, bytebankTheme, e aqui eu quero importar de novo, “Alt + Return”, “Import Library”, vou importar aqui, importei o bytebankTheme. Só para ter certeza, rodei de novo, volto lá, está tudo funcionando.

Então, vou parar por aqui, porque a partir do próximo vídeo é onde eu quero começar a fazer coisas de verdade.

Bloc e flutter bloc - Gerenciador de estado, interface com o usuário e container

Meu próximo passo é começar a pensar sobre um conceito de separar o estado da minha aplicação, do comportamento da minha aplicação, da camada de visualização da minha aplicação, do que eu quero mostrar como interface com o meu usuário, seja o que for, gráfica ou não gráfica etc.

Então, o que eu quero fazer é, nesse instante eu vou trocar o nosso Dashboard temporariamente para aquele exemplo padrão que nós vemos tanto no Flutter, quanto em outras bibliotecas, que é um contador.

Só para nós entendermos como essa separação é feita. A biblioteca que nós vamos utilizar, ela se chama “flutter_bloc”. O “flutter_bloc” está aqui, ele tem um exemplo, tem diversos exemplos de como fazer contador, porque tem diversas maneiras de você utilizar o padrão do Bloc em implementação de Bloc para Flutter, para Dart Flutter. Tem diversas maneiras de fazer isso, nós vamos fazer de uma delas, e vamos desenvolvê-la para diversas outras.

Nós vamos entender as vantagens que vão aparecendo à medida que nós vamos utilizando. Claro, podemos ir mais baixo-nível, mais eventos, na unha etc.; mais alto-nível, onde tudo isso já está encapsulado e nós só programamos as interfaces que queremos.

Vamos lá então para o código. Então, a primeira coisa é dependências. No pubspec, eu vou adicionar a minha dependência. Vou adicionar aqui antes do Cupertino, porque eu vou deixar o Cupertino Icons. Eu vou colocar que eu quero o bloc:, a partir da versão “6.0.0”. Eu não sei dizer qual a versão atual hoje em dia, especificamente. “6.0.0”, vai ser “6.0.0”.

Então, bloc: 6, desculpa, flutter_bloc: ^6.00. Salvei, vou querer dar um pub get para ter certeza que eu atualizei, vamos lá, o pub get vai funcionar.

E agora eu venho aqui e eu vou querer utilizar, então, uma página de um contador. A página de um contador vai ser o meu CounterPage, uma página de contador. Lembrando, muitas vezes vocês vão chamar isso de “Screen” em vez de “Page”. Eu chamo de “Screen”, eu chamo de “Page”, eu não coloco nada no final? Do que eu chamo? Do que eu vou chamar essas coisas?

Nós temos que definir um padrão. Defino o padrão, segue o padrão. Como aqui o modelo, desculpa, o diretório ficou chamando “screens”, eu não vou chamá-lo de “Screen”. Eu estou o chamando de “Page”, porque é muito comum vermos ele como “Page” em alguns exemplos. Estou mantendo o exemplo de “Page” aqui para nós. Então, é uma página de computador.

Vou criar esse arquivo, então, aqui eu vou dar um “Alt + Insert” no meu caso, “File”, counter_page.dart, sem o sufixo, conter.dart. Então, “counter”, aqui vai ter todas as coisas necessárias para esse nosso “CounterPage”. O nosso CounterPage, vamos pensar, CounterPage, ele é um widget, ele é stateless ou stateful?

Vamos começar com um StelessWidget. Então, lembra, um StatelessWidget tem que implementar um método, um método de “build”. Só que vamos parar para pensar: eu vou querer buildar uma página, a página vai ter algumas coisas. Ela vai precisar de um estado, ela precisa do estado dela, qual que é o estado? O estado de um contador é um número inteiro. Então, o estado é um número inteiro.

Eu poderia pensar aqui o int, um inteiro, literalmente um inteiro, ele é o tipo que vai ser representado pelo meu estado. Então, nós vamos colocar alguém para encapsular isso, os comportamentos ligados ao nosso estado. Então, como é que eu vou fazer isso?

Vou falar que eu tenho o que é chamado de um Cubit, um CounterCubit que estende um Cubit de que tipo? Qual que é o tipo do nosso estado? int. Então, cuidado, o nosso estado é um tipo int. O Cubit é quem vai gerenciar esse estado do tipo int.

Então, dentro desse meu Cubit, vou dar uma importada aqui nele, dentro do meu Cubit, eu tenho o estado, state. Então, na hora que eu iniciar liso o meu Cubit, eu tenho que ter um construtor, e o construtor vai começar chamando com o estado inicial. Qual que é o nosso estado inicial? Zero.

Então, repara que eu vou ter aqui o estado que está em um tipo, podia ser uma classe separada, no caso, é um tipo primitivo int. Poderia ser outra coisa. E o valor padrão que é zero. Então, o estado está separado disso daqui. O estado é int, é um int, e isso daqui é o que vai gerenciar esse nosso int.

Por exemplo, eu vou poder falar que eu quero incrementar, increment. Quando eu pedir para incrementar, o que eu vou fazer? Eu vou falar que eu quero mudar esse estado. Como é que eu mudo o estado? Eu emito um evento, eu falo “olha, galera, rodou um evento”. E o evento eu emito com o novo estado. Qual que é o novo estado?

É o estado antigo, lembra que eu mostrei o state? É o estado antigo, bom, está incrementando mais um. E o decrement? O decrement vai emitir um evento “estado – 1”, state -1.

Repara que eu já tenho duas coisas, eu tenho o estado, que é um int, e eu tenho gerenciador dos estados, que é essa classe com dois comportamentos diferentes, é uma classe com dois comportamentos, que é o gerenciador de estados e o estado é o int.

Como é que eu, entre aspas, troco o estado? Emitindo um novo evento para o meu sistema. Eu estou notificando o meu sistema que tem um novo estado. O “estado +1”, state + 1, ou “estado -1”, state – 1.

Então, é isso que eu estou fazendo com esse meu CounterPage. O que eu vou fazer agora? Agora que eu tenho esse meu Cubit, eu vou falar o seguinte, vou criar a minha página em si. Pensando, a minha página poderia ser, literalmente, uma View com Scaffold etc.

Então, vou escrever aqui a minha View com Scaffold e vamos para frente. Então, vou dar uma colada aqui, um return Scafold, o meu Scafold tem um appBar, que é um appBar simples cujo título é uma constate “Nosso contador”, esse aqui é o nosso contador – appBar : AppBar)title: const Text(“Nosso contador”)), aqui eu coloco uma vírgula, o nosso body é centralizado, center, ele vai ter um único child que é o nosso texto, Text, com o valor inicial.

O valor inicial, o valor para valer e tudo mais que nós quisermos. Vamos colocar um pouco de estilo aqui, nós podemos colocar um pouco de estilo que é buscar aqui o nosso textTheme, tema de texto. O tema de texto é o tema do nosso contexto, Theme.of(context).textTheme), tema de texto.

Vou importar aqui o que tem para importar, “Import”, importei tudo. Na hora que eu quiser esse texto aqui, eu só tenho que falar o style dele é o textTheme, estou dando aquela calculada, textTheme.headline2), um pouco maior, headline etc.

O meu Scaffold, então, tem esse body que é um child, mas tem os fold action bottons para mais e para menos. O meu floatingActionButton, ele é uma coluna e essa coluna tem um alinhamento no final, à direita, embaixo, então, aqui ele é um MainAxisALignment.end, e ele tem um crossAxisAlignment que também é um crossAxisALignment.end.

Então, ele vai estar no fim. E quem são os children dele aqui? Os children são dois widgets. Então, dois widgets. O primeiro widget é um FloatingActionButton, que eu tenho um child que é um Icon de adicionar, que é o mais um, Icons.add.

E quando nós pressionarmos, nós temos que fazer alguma coisa. O que eu tenho que fazer? Alguma coisa para adicionar. Então, aqui eu vou querer fazer o increment. Eu quero chamar o increment aqui.

Vou formatar esse código aqui, formatei. Isso que eu gostaria de fazer, vou pôr a vírgula, e agora eu quero ter a mesma coisa que esse FloatingActionButton, eu quero ter ele de novo aqui, só que agora com o decrement. Aqui, em vez do .add, eu vou ter o .remove, e no meio desses dois eu vou ter uma caixa SizedBox, cujo tamanho é 8.

Fixo aqui assim, só para termos um espaço. Faltou o quê? Faltou increment e o decrement. Só que para eu fazer o increment, para eu fazer o decrement, para eu colocar aqui o valor do meu estado, para eu conseguir essas três coisas, eu preciso do acesso a uma coisa, eu preciso de acesso ao meu Cubit.

Eu preciso de acesso ao meu gerenciador de estado que tem o estado lá dentro. Então, eu estou precisando disso. Como é que eu faço para acessar um Cubit? Bom, eu posso criar um Cubit aqui na unha, mas a ideia é, justamente, nós não gerenciarmos o estado, é deixar o sistema gerenciar o estado para nós e fazer tudo isso para nós, fazer as notificações e os rebuilds necessários.

Como é que eu faço isso? Primeiro, separando a nossa View da nossa página. Então, esse CounterPage aqui, na verdade, não vai ser isso. O CounterPage, por enquanto, isso daqui, na verdade, vai ser a minha CounterView.

A minha CounterView é um StatelessWidget de verdade. Agora, o meu CounterPage, ele é um StatelessWidget, ele vai reclamar, claro, nós vamos colocar aqui, por enquanto, um return CounterView. Por enquanto, nós já corrigimos isso daqui.

Então, aqui é onde nós vamos criar o nosso contador, e vai prover o contador para quem precisar, quem precisar gerenciar o estado de contador vai pode acessar um contador, isso é, todo mundo, a partir desse momento, vai ter no seu contexto acesso ao CounterCubit.

Então, o que eu estou querendo fazer? Eu estou querendo falar assim, olha, esse meu “Cubit”, esse meu “Bloc” aqui, que é um “Cubit”, esse meu “Cubit” eu quero criar, eu quero prover. Então, o que eu vou retornar não é um CounterView, é um BlocProvider. E o que o BlocProvider recebe? Têm várias maneiras de fazer isso, várias maneiras. Eu vou fazer de uma maneira, depois nós vamos ver de outras.

Então, uma das maneiras é você falar um create:, isso é, quando você criá-lo, ele vai receber aqui um contexto, e o que você vai fazer é instanciar o seu Cubit. Instanciei o meu Cubit, então, quer dizer, eu estou provendo um Cubit para os meus filhos, para as minhas filhas. Quem são meus filhos e minhas filhas? É um só, é um child. O meu child é o CounterView.

Então, a partir de agora, o que eu estou fazendo? Eu estou falando assim, olha, a minha página, que nós vamos chamar, na prática, de um CounterContext – eu vou chamar, você vai chamar, você vai ver lugares chamando em um Context, Container você também vai ver, CounterContainer.

Vou agora para o Container, você vai ver esses vários termos aparecendo na literatura, em projetos, em empresas. Então, o CounterContainer vai mostrar para nós a View, mas se ele é um CounterContainer, ele tem que ter o estalo do Counter de alguma maneira, ser capaz de prover, e o BlocProvider é quem faz isso.

O BlocProvider é quem vai juntar um Cubit, que é o estado e gerenciador de estado, com o CounterView, com a View. Ele vai juntar essas duas coisas. Então, dado o que nós recebemos esse contexto que é capaz de pegar um bloco aqui dentro, como é que eu o acesso? Tem diversas maneiras. Eu vou fazer de duas maneiras diferentes aqui para nós, para nós entendermos como isso é possível.

A primeira é aqui e aqui, em decrement e increment. O que eu vou fazer? Vou falar para o meu contexto, então repara que eu tenho o meu contexto, ele está aqui, contexto, esse é um contexto que é um contexto customizado do “Bloc”.

Eu vou falar, olha, “Bloc”, o contexto do “Bloc”, por favor, do tipo CounterCubit me dá um CounterCubit, e agora incrementa. Então, quando eu chamo contexto.bloc<CounterCubit>(), e um tipo, o que ele vai fazer? Ele vai procurar uma instância desse tipo, criar se não foi criado ainda, e disponibilizar para mim. E eu chamo o increment.

Então, se já foi disponibilizado, dá para fazer “Lazy”, não “Lazy”, tudo configurável., documentação etc., “Lazy”, parâmentro, mas explore pela medida que você precisar.

Então, dessa maneira eu consigo pedir um Cubit e acessar e utilizar. Então, aqui eu consigo simplesmente falar para o meu gerenciador de estados, “olha, precisa acontecer alguma coisa, se vira”.

E aqui é a mesma coisa, o decrement vai ser a mesma coisa. Então, essa é uma maneira de nós acessarmos. Claro, o nosso contexto tem que ter um CounterCubit. Se nós tivermos esquecido de fazer isso, vai dar erro, vai falar que não tem CounterCubit, faz sentido, não existe esse estado, mas nós provemos, nós fomos capazes de prover para o child esse CounterCubit quando necessário.

E aqui, em state, o que queremos fazer? Nós também queremos acessar. Poderia fazer um Content.Bloc? Poderia. Tem outra maneira de fazer? Tem outra maneira de fazer, vou querer mostrar outra maneira para vocês, vou pôr uma vírgula aqui de propósito, quebrei.

Então, aqui no meu child do center – poderia ser em qualquer lugar, então vou colocar aqui só um comentário para deixar claro que essa daqui é “abordagem 1 de como acessar o bloc”. E outra maneira, vai depender do que você precisa etc., é você, em vez de falar que o child é um text, você fala “olha, eu vou precisar de acesso a esse estado mesmo, então o que eu vou querer é um BlocBuilder”.

O BlocBuilder – desculpa, vamos fazer da maneira descente. “Alt + Enter”, “Wrap with Widget”, BlocBuilder. O BlocBuilder cria um widget baseado em um “Bloc”. Em que “Bloc” que nós queremos? Nós queremos um CounterCubit.

Qual que é o estado de um CounterCubit? Um int. Então, esse daqui é o meu BlocBuilder que recebe isso, só que não é um child, porque estamos buildando. Como estamos buildando, é um Builder que nós recebemos, e o Builder, na verdade, é uma função, que recebe um contexto e o estado, e essa função retorna o que nós quisermos retornar.

Eu vou escrever aqui com chaves, formatei aqui, e eu tenho aqui o meu Text que eu posso dar um return, que é uma função. Deixa eu ver se formatei tudo bonito.

Nessa variação aqui, o que eu estou fazendo? Eu estou falando “olha, meu child é alguém que vai buildar dinamicamente isso daqui”. Diferente de simplesmente “ah quero acessar um valor”. Poderia acessar um valor, poderia acessar aqui o ponto state, dá para acessar? Dá para acessar, poderia fazer dessa maneira, mas a outra maneira é falar assim, “olha, eu quero buildar algo de acordo com esse estado”.

Ele vai buildar algo de acordo com esse estado. Essa é a maneira educada do BlocBuilder.

Então, aqui na documentação, nós temos lá no “flutter_bloc” o BlocBuilder. Ele requer um “Bloc” e uma função de build. Ele faz o build de acordo com os estados novos. Essa é a sacada. Se nós deixassemos aqui fixo o estado daquela outra maneira, dessa maneira aqui. context.bloc<CounterCubit>(), quando é que ele teria que ser redesenhado? Não sei.

Mas dessa maneira que nós estamos fazendo, o que vai acontecer? Quando nós chamamos o increment, o increment chamou o emit. O que o emit faz? O emit notifica os builders que eles têm que se redesenhados. Então, esse trecho aqui, vai ser redesenhado.

Essa é a sacada. Vamos testar? Vou dar um restart aqui, eu acho que não errei nada, mas eu errei alguma coisa. “Hot restart error”, eu errei lá no main. É CounterContainer. Não importei? Achei que eu tinha importado. “Screen Counter” está lá, rodar, vamos aqui, no “Bytebank”, rodou.

0, 1, 2, 3, 2, 1, 0, -1, -2, -3, -4. Então, esse é o processo do contador utilizando o “Bloc”. Vamos mostrar aquele exemplo que eu falei que ia ser malvado, que ia ser simplesmente um Text malvado?

Eu vou deixar esse, que é o para valer, que nós vamos fazer daqui a pouco, e era simplesmente esse Text. Aqui, remove, “Wrap”, não tem o “Wrap” aqui, eu acho que tinha o remove. Não tem o remove, deveria ter, tem algum atalho que eu não sei qual é que remove o widget.

Acho que agora foi. E aqui eu quero tirar fora esse cara aqui, BlocBuilder<CounterCubit,int>( builder: (contexto, state) {, esse daqui, }, esse daqui, return, e esse daqui, ;.

Errei de novo alguma coisa a mais do que deveria. Vírgula, center, Text, o que eu errei aqui?

Eu quero tirar meu child – estou apagando coisa errada aqui, vamos ver o que eu apaguei errado aqui. Essa vírgula aqui, isso aqui é daqui, e isso aqui é do BlocBuilder, então é tudo isso daqui que eu vou querer, então, quer dizer que isso daqui vai e isso daqui vai.

Agora foi, porque o que eu vou querer é literalmente um Text. Apago essa linha, apago essa daqui, acho que agora foi. Foi, feio, mas foi. Uma vírgula aqui só para ficar mais bonito, feio, mas foi.

Então aqui, lembre-se, state, o que eu falei? Esse state eu podia fazer simplesmente, vou colocar um final state = context.bloc<CounterCubit>().state;. Estou extraindo o estado, mas esse cara aqui não tem como saber que ele tem que ser rebuildado.

Porque um Text não é notificado quando o estado muda, não tem notificação. Vou rodar, restart, restartou, zero. Estou clicando, não acontece.

Ele está executando o increment, o increment está aqui, ele emite o estado para nós, porém, isso daqui não tem que ser redesenhado. Então, a grande sacada é essa. Dessa maneira aqui, “não temos como saber quando devemos redesenhar”. Se for dessa maneira. Então, quando tem que rebuildar.

E desta outra maneira, que é a maneira que nós vamos utilizar, então, aqui, “ruim, não sabemos quando rebuildar”. E o build, ele “é notificado” como se fosse um observer, um padrão de observer, “quando deve ser rebuildado”. E ele troca o state dele, o state interno, e redesenha o que tem que redesenhar.

Só reconferindo, rodei, tem um recounter aqui, vai piscar a tela em algum instante, ou não pisca porque é zero mesmo, eu clico no mais, eu clico no menos e eu tenho funcionando.

Então, essa é a sacada, eu separei em Cubit. O que você vai ver muitas vezes é, às vezes esse increment, esse decrement, são separados em classes distintas. Os eventos são através de strings, increment e decrement. Os parâmetros que eu passo para cá são parâmetros variáveis e você não tem a compilação validando isso para você, aqui nós temos a compilação validando isso para nós.

Tem diversas variações para fazer isso, mas a ideia é essa. Então, nós temos o estado, que o int, o gerenciador do estado, que é o CounterCubit, os eventos que são as mensagens que você envia se você pensar em orientação ao objeto, que é o increment e o decrement, a interface de a comunicação com o evento, que são os parâmetros que você utiliza.

O Builder que é notificado, o Provider que cria, e um ContextBloc para você acessar o “Bloc” que você quer notificar através de algum evento.

Diversas coisas que se pode falar sobre acoplamento, desacoplamento etc. A discussão é muito profunda, eu a toquei aqui, não superficial, mas não toquei tão profundo quanto dá para nós ficarmos falando horas e horas sobre isso.

O primeiro exemplo de um contador, nós vamos aplicar isso agora ao nosso “Dashboard”.

Sobre o curso Flutter: Streams de programação reativa com BLoC

O curso Flutter: Streams de programação reativa com BLoC possui 143 minutos de vídeos, em um total de 29 atividades. Gostou? Conheça nossos outros cursos de Flutter em Mobile, ou leia nossos artigos de Mobile.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda Flutter acessando integralmente esse e outros cursos, comece hoje!

Conheça os Planos para Empresas