O incompreendido InheritedWidget

Lucas Eduardo
6 min readJul 27, 2020

--

No desenvolvimento cotidiano de uma aplicação em Flutter, sempre acabamos utilizando o StatelessWidget e o StatefulWidget para construirmos nossos widgets. Porém, também existe o InheritedWidget, você já chegou a utilizá-lo?

De acordo com a documentação do próprio Flutter, sobre o InheritedWidget

Classe base para widgets que propagam informação de forma eficiente para a árvore (de widgets).

Bom, isso nos dá algum começo para comentar sobre o assunto. Mas, o que exatamente significa "propagar informação" para a árvore de widgets? E por que o uso da palavra "eficiente"?

Se você já desenvolveu alguma aplicação em Flutter, deve ter notado que, no começo é muito comum nós passarmos informação através de construtores. As vezes, passamos a informação árvore de widget abaixo e, quando percebemos, temos 10 ou mais widgets passando uma informação via construtor.

Os InheritedWidget são uma forma de manter esse tipo de informação centralizada e de uma forma que nós consigamos acessá-la de uma forma simples e sem ter que ficar compartilhando isso de widget pra widget o tempo todo.

Digamos que nós temos um carrinho e nesse carrinho, temos a contagem de itens. Precisamos que essa parte do carrinho seja possível de ser reutilizada através de vários widgets. Podemos utilizar algumas abordagens para isso.

A primeira, seria armazenar a contagem num widget global e ir passando isso árvore de widgets abaixo via construtor. Porém, acabaríamos tendo essa informação armazenada em vários widgets diferentes e sempre que quisermos reutilizar essa parte do carrinho, teremos que passar via construtor a todo momento.

A forma que eu gostaria de mostrar aqui, seria usando um InheritedWidget. para isso. Bom, vamos ver um pouco de código agora.

Bom, nós criamos uma classe CartProvider, que estende de um InheritedWidget. Nós criamos uma variável chamada count, que irá armazenar a nossa contagem de itens no carrinho.

No nosso construtor, passamos, além do contador, uma key e um child, que passamos para a classe pai do InheritedWidget, um ProxyWidget.

O método updateShouldNotify, é utilizado pelo Flutter para saber quando a árvore de widgets deve ser reconstruída. Nesse método, você possui acesso ao oldWidget, que é o widget anterior ao momento do rebuild atual, então podemos utilizar isso para comparar se a contagem do oldWidget é diferente da contagem do widget atual e quando a contagem for diferente, significa que nossa árvore de widgets deverá ser reconstruída, pois a contagem mudou e precisamos exibi-la atualizada.

Vamos agora para o método of, onde utilizamos o dependOnInheritedWidgetOfExactType<CartProvider> a partir do contexto. Esse método é utilizado para obter o objeto mais próximo de acordo com o tipo que passamos nos <> (<CartProvider> no nosso caso). Contudo, o tipo passado, deve ser uma implementação concreta da classe InheritedWidget.

Você pode estar se perguntando, o por que do uso do método of, ao invés de obter o nosso InheritedWidget diretamente a partir do dependOnInheritedWidgetOfExactType. Bom, essa é uma convenção do Flutter e, de acordo com a documentação do próprio método, é dito que isso é para nós desenvolvedores podermos prover um fallback próprio, no caso de não ter nenhum widget no escopo. De qualquer forma, caso não tenhamos nenhum widget no escopo, o valor default retornado será nulo.

Agora, vamos praticar um pouco mais e utilizar nosso CartProvider!

Bom, criamos uma classe CartWidget, que vai ser nossa classe global, onde usamos o nosso CartProvider como primeiro widget da árvore, pois é ele que vai propagar a informação para os outros widgets. Observe que passamos um valor inicial para o nosso contador, a variável count. Além disso, nós possuímos dois botões, onde nós modificamos o valor dessa variável.

Lembra que o updateShouldNotify do nosso CartProvider, irá reconstruir a árvore quando o count, for atualizado?

Quando utilizamos o setState para atualizar a nossa variável count, definida no CartWidget e passada adiante para o CartProvider, isso vai modificar nosso count, dentro do CartProvider e nosso updateShouldNotify, irá verificar se nosso contador foi modificado, causando nossa árvore de widgets a ser recriada e atualizada com o valor modificado.

Beleza, muito legal isso. Mas… como utilizamos isso em outros widgets?

Excelente pergunta! É o que iremos abordar a seguir.

Bem simples né? Nós utilizamos o método of, que irá nos retornar uma instância do nosso CartProvider caso haja uma. A partir daí, já podemos utilizar todos os atributos que tivermos na nossa classe.

Quer usar em outro widget? Sem problemas!

Nós utilizamos da mesma forma que no exemplo anterior. Agora nós podemos incluir esses widgets no nosso CartWidget, e podemos ver que irá funcionar perfeitamente bem.

Agora, o que será que aconteceria se tentássemos recuperar o valor do nosso contador em outro widget, dentro do AnotherWidgetThatUsesCart, por exemplo?

Se formos executar o código, você verá que nesse widget também conseguimos exibir o valor do contador, pois por mais que ele seja outro widget e não esteja sendo incluído dentro do CartWidget, ele está dentro de um widget que faz parte do CartWidget, então ele está no mesmo escopo e contexto, portanto, também tem acesso ao CartProvider.

Agora, e se quisermos ir para outra página e mesmo assim ainda quisermos continuar a utilizar o nosso CartProvider?

Incluímos um botão e no onPressed, passamos um callback onde usamos o Navigator do Flutter para conseguirmos ir para uma outra página. Agora, vejamos a nossa classe AnotherPage.

Bom, tentamos usar o nosso CartProvider, como nos outros widgets. Se formos executar o código…

Bom, esse erro aconteceu, pois estamos usando o Navigator para ir para outra página, em outro escopo e contexto. Esse widget não está nem no CartWidget, e nem em um widget que está dentro do CartWidget.

Uma forma de resolver esse erro, seria envolver nossa classe AnotherPage, com um CartProvider, no momento onde usamos o Navigator.

Dessa forma, conseguiremos utilizar o contador em uma outra página.

Para desabilitar a atualização dos nossos widgets, coloque o retorno do método updateShouldNotify para false por um momento, para vermos se a atualização vai parar.

Bom, na teoria, era para os nossos widgets pararem de ser atualizados, certo? Vejam como está funcionando nossa aplicação agora:

Ué, mas por que continua atualizando? Não era pra ter parado de atualizar?

Bom, estamos utilizando o setState para atualizar nosso contador, certo? Ele irá reconstruir a nossa árvore de widgets quando atualizarmos o valor do contador, então ainda temos que arrumar um jeito de impedir essa atualização, caso desejarmos.

Para isso, basta nós acrescentarmos um construtor utilizando a palavra const em um de nossos widgets e depois também acrescentar o const onde instanciamos os widgets.

Nós utilizamos o const nessa situação, pois isso ajuda o Flutter a saber quais widgets ele deve atualizar e quais não precisam. Quando ele encontra um widget que está utilizando o const, esse widget não será reconstruído novamente quando a árvore sofrer um rebuild.

Bom, vamos ver como ficou a nossa aplicação agora:

Podemos observar que o widget onde colocamos o const, não foi reconstruído e os outros, onde não incluímos o const, foram.

Bom, esse foi o InheritedWidget. É um widget que as vezes deixamos passar despercebido, mas que pode nos ajudar muito em casos onde precisarmos compartilhar uma informação entre vários widgets e queremos fazer isso de uma forma simples.

Caso queiram conferir o código, estarei disponibilizando no meu github.

Referências

InheritedWidget

dependOnInheritedWidgetOfExactType

muito obrigado por ler ❤

--

--