Dissecando o Flutter — Ciclo de Vida de um Widget

Igor L Sambo💙🇲🇿
GDG Maputo
Published in
6 min readDec 13, 2021

Ciclo de Vida de um Widget

Tudo em Flutter é widget e antes de estudarmos sobre o ciclo de vida de um, iniciamos por entender o que é o ciclo de vida de uma aplicação no geral e como o flutter implementa esse conceito.

É agora altura de perceber mais sobre o assunto descendo mais um nível abaixo, sendo, aprender sobre o ciclo de vida de um widget.

Este artigo é parte de uma série de artigos denominada “Dissecando o Flutter”:

  1. Ciclo de Vida de uma Aplicação Flutter
  2. Ciclo de Vida de um Widget (este artigo)

Para iniciar, devemos recordar que temos dois tipos de widgets, StatelessWidgets e StatefulWidgets, onde temos apenas um único estado, imutável e podemos ter vários estados enquanto usamos a app, mutável, respectivamente. No caso, StatefulWidgets são os que permitem monitorar e fazer uso do ciclo de vida de um ciclo de vida de um widget. Considere a imagem a seguir.

fonte: https://flutteragency.com/what-is-flutter-application-lifecycle/

Vamos dissecar a imagem?

1. Constructor

O constructor é o primeiro elemento a ser chamado, antes mesmo do createState(), porém, não retorna nenhum objecto, sendo então considerado como primeira função o createState().

2. createState()

@override
State<MyHomePage> createState() => _MyHomePageState();

No momento em que o Flutter “constrói” um StatefulWidget chama imediatamente o createState() criando um novo estado do mesmo. Se reparamos na imagem, e também no trecho de código conseguimos ver que temos um StatefulWidget e também um State, e a pergunta a colocar seria, porquê duas classes separadas e não apenas uma como em StatelessWidgets?
Respondendo de forma simples, é pela performance, pois, é menos dispendioso para o Flutter reconstruir um widget mutável que o State que é construído apenas uma vez, ou seja, o State, não é descartado em cada build.

3. initState()

Logo a seguir a criação do widget, o primeiro método a ser chamado é o initState(), logo a seguir ao classConstructor. Este método é chamado apenas uma vez, no caso, apenas quando o widget é criado e ao longo das alterações feitas no mesmo este não é mais referenciado, sendo, por isso usado para inicialização de dados que dependem do BuildContext específico, inicializa propriedades que dependem dos widgets pais e subscrever dados vindo de streams, futures, ChangeNotifiers entre outros.

@override
void initState() {
super.initState();

}

É importante notar que deve sempre se referenciar o super.initState() e deve ser a primeira instrução do método.

4. setState()

Toda vez que o widget criado sofre uma alteração, seja pelo próprio framework ou por acção do utilizador (implementação do desenvolvedor) por meio do setState() este informa ao framework que houve esta alteração e o mesmo deve ser reconstruído, fazendo trigger do build() relativo ao contexto em que se encontra.
Qualquer chamada ao setState e as acções dentro do mesmo não devem ser assíncronas, o Flutter irá retornar um erro. Este pode ser chamada quantas vezes forem necessárias e esta acção é pouco dispendiosa (barata) para o Flutter.

void _incrementCounter() {
setState(() {
_counter++;
});
}

5. didUpdateWidget()

Suponhamos que temos dois widgets e um é “pai” do outro, neste caso quando o widget pai modifica algo, o didUpdateWidget é chamado, de modo que a fazer saber da mudança. Logo a seguir o build() é triggered, por isso, é importante notar que qualquer setState dentro do didUpdateWidget é redundante.

A seguir, um exemplo ilustrativo, tendo modificado o exemplo original dado pelo Flutter, de modo a simplificar a percepção.

6. didChangeDependencies()

O didChangeDependencies() é chamado logo a seguir ao initState() quando o widget é construído e também no caso de algum dado vindo de umInheritedWidget, do qual dependemos for modificado.
Para melhor percepção, vamos usar do mesmo exemplo porém, iremos actualizar a cor do botão que era suposto incrementar, criando assim mais dois widgets para ilustrar este caso.

  • color_button_state é por onde iremos actualizar a cor do botão e;
  • inherited_page será o nosso ponto de entrada, para controlar as actualizações.

Primeiro, criamos o nosso inherited_page, estendendo o mesmo do InheritedWidget.

class InheritedPage extends InheritedWidget {
final ColorButtonStateState state;

InheritedPage({required this.state, required Widget child})
: super(child: child);

// informa aos widgets dependentes sobre alguma actualização
@override
bool updateShouldNotify(InheritedPage old) => true;

//criação de um método "of" para nos facilitar o acesso para os atributos do nosso InheritedWidget
static InheritedPage? of(BuildContext context) =>
context.dependOnInheritedWidgetOfExactType(aspect: InheritedPage);
}

De seguida, o nosso color_button_state para controlar as actualizações de estado, e iremos usar como recurso o nosso InheritedWidget, informando assim sobre a mudança de estado aos widgets que estarão a depender deste dado, no caso será o nosso botão.

A implementação ficará assim

7. deactivate()

Sempre que o estado de um widget é removido este método é chamado, chamado logo antes dispose() (que falaremos a seguir), indicando que o widget será removido, e de momento não está disponível na view.

8. dispose()

Ao contrário do deactivate(), o dispose() indica remoção definitiva do widget da widget tree, exemplo, quando saímos de uma tela, aqui podemos cancelar os nossos estados, tirar subscrições de streams, entre outros casos de uso que nós podemos implementar de acordo com as necessidades do projecto.

9. build()

Este é o método que mais é usado e é chamado logo a seguir ao didChange Dependencies, ou seja, antes que qualquer estado carregue suas dependências o initState() é triggered, logo que estas são carregadas temos o didChangeDependencies() e assim que estas estão totalmente carregadas o build() contendo o mapeamento de cada widget na Widget Tree, portanto, esta operação não é dispendiosa, pois, apenas os widgets modificados irão realmente ser redesenhados na tela. É o método mais importante e principal da aplicação, por ser responsável em redesenhar a UI sempre que qualquer que seja o elemento sofra alguma mudança. Para todos os exemplos que nós tivemos para actualização de texto e cor, este sempre fez parte, garantido que os widgets são visíveis como desejado.

Notas finais

Tal e qual a aplicação/engine flutter, os widgets que o compõem também possuem um ciclo de vida e entender o mesmo, permite-nos tirar proveito de cada estágio em que estes podem encontrar-se e dependendo do nosso caso de uso implementar a nossa própria lógica. Entender cada um destes estágios e sua aplicação dar-nos-á grande vantagem quando formos projectar o nosso projecto e tivermos que gerir um determinado dado de acordo com o comportamento do nosso utilizador e acções na aplicação, assim como as chamadas a alguns serviços.

Espero que tenha aprendido com este artigo e que se tenha divertido enquanto lia.

Agradecia que deixasse ficar o seu comentário, assim como sugestões para próximos tópicos a abordar, assim como sua curiosidade, podendo fazê-lo pela caixa de comentários, email igorlsambo1999@gmail.com ou twitter @lsambo02.

Obrigado por acompanhar até ao fim e espero por você no próximo artigo!

--

--