State é o objeto central de gerenciamento de dados no Flutter, associado ao StatefulWidget e responsável por armazenar informações mutáveis e construir a interface. De acordo com a documentação oficial do Flutter (Flutter.dev, 2026), o State existe durante todo o ciclo de vida do widget e sobrevive às suas reconstruções, garantindo a consistência dos dados entre as atualizações da UI. Ao contrário do próprio widget, o State pode modificar seus campos e iniciar a reconstrução através da chamada setState.
Principais pontos
State é um objeto na arquitetura Flutter que armazena dados mutáveis de um StatefulWidget e determina como esses dados são exibidos na interface. Cada StatefulWidget, ao ser inserido na árvore, cria exatamente um objeto State através do método createState. O State existe independentemente do widget: se o pai reconstruir o StatefulWidget com novos parâmetros, o State permanece o mesmo e recebe o widget atualizado através da propriedade widget.
De acordo com a Visão Geral Arquitetônica do Flutter (Google, 2026), a separação de Widget e State é uma decisão arquitetônica deliberada que permite ao framework reutilizar elementos da árvore. O widget (uma descrição leve) pode ser criado e destruído várias vezes, mas o State (um objeto pesado com dados) permanece na memória enquanto o elemento estiver na árvore. Isso evita a perda de dados durante reconstruções frequentes de widgets pais.
State implementa a interface StatefulWidget através de genéricos: class _MyState extends State<MyWidget>. O genérico vincula State a um tipo específico de StatefulWidget, fornecendo acesso type-safe aos seus campos através da propriedade widget.
O objeto State é armazenado em StatefulElement — uma camada intermediária entre Widget e RenderObject. StatefulElement cria State através de createState, mantém uma referência a ele e passa State como proprietário. O Element só é destruído quando o widget é removido da árvore — até então, o State vive na memória.
O ciclo de vida do State é determinístico e consiste em uma sequência estrita de chamadas. Entender essa sequência é a base para o gerenciamento correto de recursos e prevenção de vazamentos de memória.
initState é chamado primeiro quando State é criado. Neste método, controladores, assinaturas de stream, temporizadores e valores iniciais de campos são inicializados. Chamar super.initState() na primeira linha é obrigatório. No estágio initState, a árvore de widgets ainda não está totalmente montada, então métodos como MediaQuery.of(context) podem não funcionar corretamente.
didChangeDependencies é chamado após initState e sempre que as dependências InheritedWidget mudam. É aqui, e não em initState, que se deve chamar MediaQuery.of(context) ou Theme.of(context), pois a essa altura a árvore já está montada. Este método também é chamado se o widget se move para um contexto diferente onde InheritedWidget fornece outros valores.
build é o método principal de State que retorna uma árvore de widgets. É chamado após initState, após didChangeDependencies e após cada setState. O método build não deve ter efeitos colaterais — ele apenas descreve a interface com base nos valores atuais dos campos do State.
didUpdateWidget é chamado quando o pai reconstrói o StatefulWidget com novos parâmetros. State obtém acesso ao widget antigo através de oldWidget e pode compará-lo com o novo. Se os parâmetros mudaram, pode-se atualizar o estado, carregar novos dados ou reiniciar uma animação.
dispose é o método final onde todos os recursos são liberados: controladores, assinaturas, temporizadores. Após dispose, State é marcado como morto: mounted retorna false, chamar setState lança uma exceção. Chamar super.dispose() na última linha do método é obrigatório.
| Método | Quando chamado | super obrigatório |
|---|---|---|
| initState | Ao criar State | Sim, na primeira linha |
| didChangeDependencies | Após initState e ao mudar InheritedWidget | Sim |
| build | Após initState, didChangeDependencies, setState | Não |
| didUpdateWidget | Ao receber novo widget do pai | Sim |
| setState | Por chamada do desenvolvedor | Não |
| dispose | Ao remover da árvore | Sim, na última linha |
O mecanismo de funcionamento do State é baseado em três princípios-chave: associação com Element, reatividade através de setState e acesso ao pai via propriedade widget. Quando Flutter constrói a árvore de elementos e encontra um StatefulElement, ele chama createState do widget associado. O State criado é armazenado no elemento e existe até que o elemento seja removido.
Ao chamar setState, State se marca como sujo e agenda uma reconstrução para o próximo quadro. Importante: setState não chama build imediatamente — ele apenas registra a necessidade de reconstrução. Flutter coleta todos os elementos sujos do quadro atual e os reconstrói em lote, o que otimiza o desempenho. Após a chamada build, State retorna ao estado limpo.
A propriedade widget permite que State leia os parâmetros passados ao construtor StatefulWidget. Como StatefulWidget é imutável (como StatelessWidget), seus campos não mudam — quando os parâmetros mudam, o pai cria um novo widget e State o recebe através de didUpdateWidget. Isso garante que State sempre trabalhe com os dados atuais do pai.
Exemplo básico de State com um campo modificado por um temporizador. Demonstra initState, setState e dispose:
class _TimerWidgetState extends State<TimerWidget> {
int _seconds = 0;
Timer? _timer;
@override
void initState() {
super.initState();
_timer = Timer.periodic(
const Duration(seconds: 1),
(_) => setState(() => _seconds++),
);
}
@override
void dispose() {
_timer?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Text('$_seconds seconds elapsed');
}
}
Exemplo usando a propriedade widget para acessar parâmetros do pai e reagir às suas mudanças via didUpdateWidget:
class _GreetingState extends State<GreetingWidget> {
String _displayName = '';
@override
void initState() {
super.initState();
_displayName = _formatName(widget.name);
}
@override
void didUpdateWidget(GreetingWidget oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.name != oldWidget.name) {
setState(() {
_displayName = _formatName(widget.name);
});
}
}
String _formatName(String name) => name.trim().isEmpty ? 'Guest' : name;
@override
Widget build(BuildContext context) {
return Text('Hello, $_displayName!');
}
}
No segundo exemplo, State rastreia mudanças no parâmetro de entrada name e reformata a exibição apenas quando ocorre uma mudança real. Sem a verificação widget.name != oldWidget.name, o método seria chamado em cada reconstrução do pai, mesmo que o nome não tivesse mudado — trabalho desnecessário para o framework.
State e StatefulWidget são duas classes diferentes na arquitetura Flutter que desempenham papéis distintos. StatefulWidget é um invólucro imutável leve que descreve a configuração do widget e cria State. State é um objeto pesado que armazena dados mutáveis, gerencia assinaturas e constrói a UI. Essa separação permite ao Flutter destruir e criar widgets sem perder o estado.
Todos os campos de StatefulWidget devem ser final e definidos no construtor — eles não mudam após a criação. State, por outro lado, pode modificar seus campos a qualquer momento, mas todas as mudanças devem ser precedidas por uma chamada setState para que o Flutter saiba da necessidade de reconstrução. Esta é a diferença chave: StatefulWidget é “o que mostrar”, State é “como mostrar e quais dados usar”.
De acordo com a análise do código fonte do Flutter (Flutter SDK, 2026), StatefulWidget contém apenas um campo obrigatório — createState, enquanto State tem acesso a BuildContext, pode se inscrever em streams, gerenciar animações e controladores. Recomenda-se manter StatefulWidget o mais simples possível, transferindo toda a lógica para State.
A separação de Widget e State é uma decisão arquitetônica que garante a imutabilidade da configuração. Se StatefulWidget armazenasse o próprio estado, o estado seria perdido em cada reconstrução do pai. Ao mover o estado para um objeto separado, Flutter garante que os dados sobrevivam às reconstruções, enquanto os widgets permanecem leves e comparáveis.
O objeto State é isolado — ele não tem acesso direto ao State de outros widgets. Para troca de dados entre widgets, são usados InheritedWidget ou ferramentas externas de gerenciamento de estado: Provider, Riverpod, Bloc, Redux. Cada abordagem resolve o problema de forma diferente: InheritedWidget funciona através da árvore de widgets, Provider através de um contêiner DI, Bloc através de streams de eventos.
A escolha da ferramenta depende da escala do projeto. Para uma aplicação pequena, InheritedWidget e State local são suficientes. Para projetos médios e grandes, Riverpod ou Bloc são recomendados — eles garantem testabilidade, previsibilidade e separação da lógica da UI. State é então usado apenas para dados locais do widget (foco, scroll, animação).
De acordo com a Pesquisa da Comunidade Flutter 2025 (Flutter Foundation, dezembro de 2025), Riverpod é a solução de gerenciamento de estado mais popular em novos projetos (38%), seguido por Bloc (31%) e Provider (22%). Todas as três ferramentas são compatíveis com State e não exigem abandonar o ciclo de vida padrão.
O primeiro erro é esquecer de verificar mounted antes de setState em um callback assíncrono. Quando um widget é removido da árvore (por exemplo, o usuário navegou para fora), mas uma operação assíncrona (requisição HTTP) ainda está em execução, após sua conclusão o State já está morto. Chamar setState em um State morto lança uma exceção. A verificação if (mounted) setState(...) resolve o problema.
O segundo erro é inicializar dependências InheritedWidget em initState em vez de didChangeDependencies. Em initState, o contexto ainda não está montado, então MediaQuery.of(context) lançará uma exceção. Todas as dependências InheritedWidget devem ser configuradas em didChangeDependencies ou em build.
O terceiro erro é mutar campos sem chamar setState. Se um desenvolvedor altera um campo State sem setState, Flutter não saberá da mudança e a UI não será atualizada. Por exemplo: _list.add(item) sem um setState((){}) subsequente modificará a lista, mas a tela permanecerá a mesma.
Padrão de segurança para operações assíncronas em State:
Future<void> _fetchData() async {
final data = await ApiService.fetch();
if (mounted) {
setState(() => _data = data);
}
}
Verificar mounted garante que setState seja chamado apenas em um State vivo, evitando a exceção “setState called after dispose”.
Perguntas frequentes
StatefulWidget é uma configuração imutável de widget, enquanto State é um objeto mutável que armazena dados e gerencia o ciclo de vida. O widget pode ser recriado, State não. StatefulWidget cria State através de createState.
Exatamente um. O método createState é chamado uma vez quando o StatefulWidget é inserido pela primeira vez na árvore. Mesmo que o pai reconstrua várias vezes, o objeto State permanece o mesmo até que o tipo ou a Key do widget mude.
mounted é um sinalizador booleano que mostra se o State está na árvore de widgets. Após chamar dispose, mounted se torna false. É usado para verificação antes de setState em callbacks assíncronos para evitar exceções.
Não. State está sempre vinculado a um StatefulWidget específico através de genéricos: State<T extends StatefulWidget>. Criar State diretamente, sem associação com um widget, é arquitetonicamente impossível.
Uma exceção é lançada: “setState called after dispose”. Após dispose, State é considerado morto e qualquer tentativa de reconstruir a UI através de setState é proibida. A solução é verificar mounted antes de cada setState.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também