setState() é o método chave de State no Flutter, notificando o framework sobre alterações de dados e acionando a reconstrução da interface. De acordo com a documentação oficial do Flutter (Flutter.dev, 2026), setState é o principal mecanismo de reatividade em StatefulWidget: sem sua chamada, a UI não saberá sobre alterações nos campos de State e permanecerá no estado anterior. O método aceita um VoidCallback, dentro do qual o desenvolvedor modifica os campos mutáveis, após o que Flutter chama automaticamente build para reconstruir o widget.
Pontos principais
setState() é um método embutido da classe State no Flutter, projetado para notificar o framework de que o estado interno do widget mudou e a UI precisa ser reconstruída. Sem chamar setState, o Flutter não sabe sobre as mudanças — mesmo que os campos de State tenham sido modificados, a interface permanecerá inalterada até a próxima reconstrução forçada pelo pai.
Assinatura do método: void setState(VoidCallback fn). O callback é executado de forma síncrona dentro de setState, e somente após sua conclusão o State é marcado como sujo. Isso garante que todas as alterações sejam aplicadas atomicamente antes da reconstrução. De acordo com a Especificação da Linguagem Dart (Dart Team, 2026), a atomicidade do setState evita condições de corrida onde build poderia ver um estado parcialmente atualizado.
setState não aceita argumentos, não retorna valor e não pode ser sobrescrito. É um método final (selado) da classe State. O desenvolvedor não pode alterar seu comportamento — apenas usá-lo como pretendido. Tentar chamar setState fora de State (por exemplo, de outra classe) é impossível porque o método é declarado na classe State.
Um equívoco comum é pensar que setState muda o estado por si só. Isso não é verdade. setState apenas chama o callback passado (no qual o desenvolvedor modifica os campos) e então sinaliza ao framework a necessidade de build. O callback é obrigatório — passar null ou um callback vazio causará um erro.
O mecanismo de funcionamento do setState() pode ser dividido em quatro etapas. Primeira — chamar o método com um callback. Segunda — execução síncrona do callback, dentro do qual os campos de State são modificados. Terceira — State é marcado como sujo em um campo especial _dirty. Quarta — ao final da microtarefa atual, o Flutter itera por todos os elementos sujos e chama seus builds na ordem de aparecimento na árvore.
Um detalhe importante: setState não chama build imediatamente. Flutter usa uma estratégia de atualização em lote: todos os elementos sujos são coletados e reconstruídos em um único quadro. Isso significa que se setState for chamado várias vezes dentro de um único bloco síncrono, build será executado apenas uma vez — após todas as alterações estarem completas. Essa otimização evita múltiplas reconstruções por quadro.
De acordo com o Flutter Engine Team (Google, 2025), o mecanismo de flag sujo é baseado na passagem BuildOwner._dirtyElements. Cada StatefulElement sujo é adicionado à lista e processado no estágio de atualização do quadro. Se um widget foi removido da árvore antes do processamento, ele é automaticamente excluído da lista de elementos sujos.
Exemplo básico de setState() com incremento de contador. Demonstra o uso correto: modificar um campo dentro do callback:
class _CounterState extends State<CounterWidget> {
int _count = 0;
void _increment() {
setState(() {
_count++; // mutando o campo dentro do callback
});
}
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: _increment,
child: Text('$_count'),
);
}
}
Exemplo com campo de texto e controlador — setState() para gerenciamento de visibilidade de senha:
class _PasswordFieldState extends State<PasswordField> {
bool _obscured = true;
final _controller = TextEditingController();
void _toggleVisibility() {
setState(() {
_obscured = !_obscured;
});
}
@override
Widget build(BuildContext context) {
return TextField(
controller: _controller,
obscureText: _obscured,
decoration: InputDecoration(
suffixIcon: IconButton(
icon: Icon(_obscured ? Icons.visibility : Icons.visibility_off),
onPressed: _toggleVisibility,
),
),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
Neste exemplo, setState() apenas altera o campo booleano _obscured, o que aciona a reconstrução do TextField com um novo ícone e modo de exibição. O controlador de texto não é recriado — é inicializado uma vez em initState e liberado em dispose.
Se você precisa alterar vários campos, todas as alterações devem ser feitas dentro de um único setState. Isso garante que build verá um estado consistente:
setState(() {
_isLoading = false;
_items = newItems;
_error = null;
});
Três campos são alterados em um callback — build será executado uma vez e verá todas as alterações simultaneamente. Se cada chamada fosse um setState separado, build ainda seria executado apenas uma vez graças ao processamento em lote de elementos sujos.
Uma das nuances mais importantes do setState() é seu comportamento com operações assíncronas. O callback do setState é executado de forma síncrona, mas se await for chamado dentro dele, o código após await será executado depois que setState já tiver concluído seu trabalho. Isso significa que alterações de campo após await não serão capturadas pelo setState atual.
A abordagem correta: a operação assíncrona é realizada fora do setState, e setState é chamado após sua conclusão. Todo o código entre o recebimento do resultado e a chamada a setState é executado em um contexto síncrono após await:
// CORRETO: await fora de setState
Future<void> _loadData() async {
final result = await ApiService.fetchData();
setState(() {
_data = result;
_isLoading = false;
});
}
// ERRADO: await dentro de setState — sem garantia de atualização
void _loadDataWrong() {
setState(() async {
_data = await ApiService.fetchData(); // setState retorna antes de await completar
_isLoading = false; // este código não é capturado por setState
});
}
De acordo com a documentação do Flutter (Dart async patterns, 2026), passar um callback assíncrono para setState é um antipadrão porque setState espera um VoidCallback (função síncrona), enquanto uma função assíncrona retorna um Future que é ignorado. Alterações após o primeiro await em tal callback não serão tratadas corretamente pelo framework.
Antes de chamar setState() após uma operação assíncrona, sempre verifique mounted:
if (mounted) {
setState(() => _data = data);
}
Se o widget foi removido da árvore durante a operação assíncrona, mounted se tornará false e setState não será chamado. Isso evita exceções e vazamentos de recursos.
setState() é um mecanismo conveniente, mas potencialmente caro se usado sem pensar. Cada chamada a setState reconstrói todo o widget e todos os seus descendentes (se não forem const). Em árvores profundas ou com chamadas frequentes, isso pode levar a quedas de FPS.
Principais estratégias de otimização: minimizar a área de reconstrução (extrair partes mutáveis da UI em StatefulWidgets separados), usar const para filhos imutáveis e evitar chamar setState em widgets pai se apenas um pequeno detalhe de UX mudou. Se o estado é atualizado em alta frequência (animação, fluxo de dados), considere AnimatedBuilder ou ValueListenableBuilder.
De acordo com as Melhores Práticas de Desempenho do Flutter (Flutter.dev, fevereiro de 2026), a criação de perfil de aplicações reais mostra que até 40% de todas as chamadas a setState podem ser substituídas por widgets filhos const ou construtores reativos (StreamBuilder, FutureBuilder). Isso reduz o tempo médio de construção do quadro em 15–25%.
| Cenário | Alternativa | Vantagem |
|---|---|---|
| Animação | AnimatedBuilder | Reconstrói apenas o widget animado |
| Fluxo de dados | StreamBuilder | Reage a cada elemento do fluxo |
| Resultado futuro | FutureBuilder | Gerencia estados de carregamento/erro |
| Valor local | ValueListenableBuilder | Reage a alterações de um único valor |
Apesar da versatilidade do setState(), em grandes projetos ele é usado principalmente para estado local. Para estado global ou compartilhado, são usadas soluções especializadas, cada uma substituindo ou encapsulando setState.
Provider usa ChangeNotifier + notifyListeners como análogo ao setState, mas com a capacidade de inscrever vários widgets. Bloc usa Streams — o estado é alterado adicionando eventos a um StreamController. Riverpod combina abordagens, fornecendo gerenciamento local (StateProvider) e assíncrono (AsyncNotifier) sem vinculação a StatefulWidget. Todas as três abordagens eliminam a necessidade de chamar setState manualmente — as atualizações da UI ocorrem automaticamente quando os dados mudam.
De acordo com a Pesquisa da Comunidade Flutter 2025 (Flutter Foundation, dezembro de 2025), 74% dos desenvolvedores usam pelo menos uma ferramenta de gerenciamento de estado além do setState. Ao mesmo tempo, 92% continuam a usar setState para dados locais de campos de texto, caixas de seleção ou contadores simples — isso é considerado uma boa prática.
O primeiro e mais perigoso erro é chamar setState após dispose. Uma operação assíncrona iniciada em initState, o usuário saiu da tela, o widget foi removido e o callback assíncrono chama setState — o aplicativo trava com uma exceção. Solução — sempre verifique mounted antes de chamar.
O segundo erro é chamar setState dentro de build. Isso leva a um loop infinito: build → setState → sujo → build → setState → ... Flutter não bloqueia tal chamada (você obterá um StackOverflowError). setState só pode ser chamado em resposta a um evento (pressão de botão, conclusão de Future, dados de um fluxo).
O terceiro erro é modificar campos de State sem chamar setState. O desenvolvedor escreve _count++ e espera que a UI atualize. Flutter não consegue rastrear alterações de campo automaticamente — ele precisa de um sinal explícito via setState. Esta é uma diferença fundamental de frameworks reativos como Vue.js, onde alterações de dados acionam automaticamente atualizações.
O quarto erro é chamar setState com um callback assíncrono (lambda async). Como descrito na seção de assincronia, alterações após await não serão capturadas, levando a bugs difíceis de reproduzir. Use um callback síncrono e chame setState após await.
mounted em callbacks assíncronosPerguntas frequentes
setState() notifica o Flutter de que os dados internos do StatefulWidget mudaram e a UI precisa ser reconstruída. O método aceita um callback, o executa de forma síncrona, marca o widget como sujo e agenda a chamada a build no próximo quadro.
A UI não será atualizada. Flutter não rastreia alterações de campo automaticamente. O valor do campo muda na memória, mas o widget permanece em seu estado anterior até a próxima reconstrução forçada pelo pai.
Não. Isso leva a um loop infinito: build chama setState, que marca o widget como sujo e chama build novamente. Flutter não bloqueia esta situação — o aplicativo falhará com StackOverflowError.
Build será executado uma vez. Flutter coleta todos os elementos sujos e os reconstrói em lote no final do quadro. O segundo setState antes do processamento simplesmente adiciona o elemento à mesma lista de elementos sujos — nenhuma reconstrução repetida ocorre.
mounted é uma flag booleana que indica que o widget ainda está na árvore. Se setState for chamado após uma operação assíncrona sem verificar mounted e o widget já tiver sido removido — o aplicativo trava com a exceção "setState called after dispose".
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