2. Flutter: StatelessWidget, StatefulWidget e Ciclos de Vida

Qual seria a diferença entre StatelessWidget e StatefulWidget? Como eles nascem?

2. Flutter: StatelessWidget, StatefulWidget e Ciclos de Vida

StatelessWidget

Os componentes Stateless não tem estado, são estáticos, ou seja, depois de carregado não consegue mais atualizar o estado.

import 'package:flutter/material.dart';

void main() {
  runApp(HomePageStateless());
}

class HomePageStateless extends StatelessWidget {
  String texto = 'Olá';

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Text(texto),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            texto = 'Cliquei no Floating Button';
          }),
      ),
    );
  }
}

O MaterialApp() é o componente principal do Flutter, onde você coloca as rotas, themas e tudo que você precisa para trabalhar seu aplicativo.

O Scaffold() é o widget principal para montar nossa tela, se não o colocarmos, a tela ficará com uma aparência escura. É nesse componente que existe o AppBar(), aquela barra que fica lá em cima do app, entre outras coisas que ele gerencia para que você possa usar em seu aplicativo.

Observe que no código de exemplo acima o bottão criado "Floating Action Button" realiza a ação de alterar a variável String texto, porém irá apresentar algumas informações:

  1. Como o StatelessWidget é um componente imutável suas variáveis devem também ser, então irá pedir para que você coloque suas variáveis como final;
  2. Ao atribuirmos o modificador final a variável String texto, ocorrerá outro erro que aparecerá na função executada ao clicar no floatingActionButton, pois, se a variável é imutável, não se pode alterá-la, por isso irá aparecer tal erro.

Percebe-se que não tem como alterar, em regra, o estado de um StatelessWidget.

StatefulWidget

É o componente mutável de flutter:

import 'package:flutter/material.dart';

void main() {
  runApp(HomePageStateful());
}

class HomePageStateful extends StatefulWidget {
  @override
  _HomePageStatefulState createState() => _HomePageStatefulState();
}

class _HomePageStatefulState extends State {
  String texto = 'Olá';

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Text(texto),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            texto = 'Cliquei no Floating Button';
          }),
      ),
    );
  }
}

Pronto, mas, mesmo assim se eu clicar no botão não irá ocorrer nada, pois o StatefulWidget disponibiliza uma função setState() que é a responsável para que eu possa alterar o estado da aplicação, que seria alterar a variável String texto.

import 'package:flutter/material.dart';

void main() {
  runApp(HomePageStateful());
}

class HomePageStateful extends StatefulWidget {
  @override
  _HomePageStatefulState createState() => _HomePageStatefulState();
}

class _HomePageStatefulState extends State {
  String texto = 'Olá';

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Text(texto),
        floatingActionButton: FloatingActionButton(onPressed: () {
          setState(() {
            texto = 'Cliquei no Floating Button';
          });
        }),
      ),
    );
  }
}

No StatelessWidget não existe esta função anônima chamada setState().

Esta é a diferença básica entre estes dos componentes. Para ver o código acima em execução entre aqui no DartPad.

Clico de Vida dos Compontens

O componente StatelessWidget a partir do momento que é chamado ele executa o ciclo:

StatelessWidget -> constructor -> build

Já o StatefulWidget funciona da seguinte forma:

StatefulWidget - > createState() -> constructor -> initState() -> didChangeDependencies() -> build -> setState() -> build

build

Sempre que for necessário reconstruir um componente, o build será chamado.

initState()

É invocado somente uma vez e serve para que, antes de construir a tela, possa alimentar os estados do jeito que você precisa. Porém, como a tela ainda não foi montada, não existe aqui o BuildContext context, então não poderia abrir uma tela, por exemplo.

didChangeDependencies()

Após o initState() é executado o didChangeDependencies() onde também pode ser feito a alteração do estado, também somente é chamado uma vez.

setState()

O StatefulWidget, como vimos no início do artigo, tem o setState() que serve para atualizar a tela ou notificar o widget sobre a alteração que houve e quando nos o acionamos, ele invoca outro método o didUpdateWidget() e por último o build.

didUpdateWidget()

É o método chamado quando o "pai" dele é reconstruído, fazendo com que seja reconstruído o componente filho também, porém será chamado o didUpdateWidget(), mas somente ocorre se o Widget "pai" for reconstruído e não se o Widget "filho" for reconstruído.

@override
void didUpdateWidget(HomePageStateful oldWidget) {
  super.didUpdateWidget(oldWidget);
  print('didUpdateWidget');
}

Mas para que serve? Geralmente quando se quer resetar um valor inicial de algo, uma varável, por exemplo.

dispose()

A última parte do ciclo de vida é feito para limpeza e é necessário quando há stream ou controladores e se é necessário chamar o close() dele, evitando assim o uso excessivo da memória, veremos mais a respeito dele mais a frente.

Só é chamado quando a página, pilha, que você está sair da memória, quando o componente deixar de existir.