FutureBuilder é um widget no Flutter que reconstrói automaticamente sua interface com base no estado atual do AsyncSnapshot obtido de um Future fornecido. Ao contrário de chamar setState manualmente após await, o FutureBuilder fornece uma abordagem declarativa: ele se inscreve no Future no primeiro render e chama a função builder em cada mudança de estado — carregamento, erro ou dados prontos. De acordo com a Referência da API Flutter (2026), o FutureBuilder é especialmente útil para carregar dados da rede, ler de um banco de dados e quaisquer operações assíncronas onde a UI precisa exibir um indicador de carregamento, mensagem de erro ou conteúdo pronto.
Pontos Principais
FutureBuilder é um widget integrado do Flutter do pacote widgets que recebe um Future
Ao contrário do StreamBuilder, que trabalha com fluxos de dados (Stream), o FutureBuilder é projetado para operações assíncronas únicas: requisição HTTP, leitura de arquivo, consulta a banco de dados. O FutureBuilder gerencia a inscrição no Future por conta própria: na primeira construção, ele inicia o Future e rastreia sua conclusão. Quando o widget é destruído, o FutureBuilder não cancela o Future — essa é responsabilidade do desenvolvedor.
De acordo com o Flutter Cookbook (2026), o FutureBuilder é recomendado para casos onde uma operação assíncrona é executada uma vez na inicialização da tela. Para operações recorrentes ou fluxos de dados, use StreamBuilder. Ambos os widgets seguem o mesmo padrão de UI Reativa, mas o FutureBuilder é otimizado para requisições únicas.
A implementação interna do FutureBuilder se inscreve no Future usando Future.then e catchError. Ao iniciar, o FutureBuilder define connectionState como ConnectionState.waiting e chama o builder com dados vazios. Ao concluir com sucesso, connectionState muda para ConnectionState.done com dados. Em caso de erro, snapshot.error é preenchido com o objeto de erro. Cada mudança desencadeia uma reconstrução do widget.
AsyncSnapshot é um objeto contêiner que o FutureBuilder passa para a função builder em cada mudança de estado. Ele contém todas as informações sobre o status atual da operação assíncrona: se o carregamento está em andamento, quais dados foram recebidos ou se ocorreu um erro. Entender o AsyncSnapshot é chave para construir corretamente a UI com FutureBuilder.
| Propriedade | Tipo | Descrição |
|---|---|---|
| connectionState | ConnectionState | Estado atual da conexão (none, waiting, active, done) |
| data | T? | Dados recebidos do Future (null até a conclusão ou em erro) |
| error | Object? | Objeto de erro se o Future concluiu com uma exceção |
| hasData | bool | true se data não é null e connectionState é ConnectionState.done |
| hasError | bool | true se o Future concluiu com erro |
O enum ConnectionState define o estágio de uma operação assíncrona. None — estado inicial quando o Future ainda não foi iniciado (raramente usado, tipicamente na primeira construção sem initialData). Waiting — o Future está executando, dados ainda não recebidos. Active — usado apenas pelo StreamBuilder para streams com dados parciais. Done — o Future foi concluído, dados disponíveis via snapshot.data ou erro via snapshot.error.
O tratamento adequado de todos os estados do AsyncSnapshot na função builder é um requisito obrigatório para código de produção. Se você não tratar o estado waiting, o usuário verá uma tela vazia durante o carregamento. Se não tratar hasError, o usuário receberá uma Exceção sem explicação. O padrão recomendado: verificar hasError → verificar hasData → mostrar carregamento por padrão.
FutureBuilder pode ser usado em vários padrões padrão, cada um resolvendo uma tarefa específica. Vamos ver os principais cenários: carregar dados na inicialização, carregar com cache, requisições paralelas e tratamento de erros com repetição.
O padrão mais comum — FutureBuilder no método build de um StatefulWidget ou StatelessWidget. O Future é passado de initState ou criado diretamente no build. É importante não criar o Future no método build em cada reconstrução — isso levará a requisições repetidas. Use um Future armazenado em um campo do State.
Para evitar requisições repetidas, o FutureBuilder pode ser combinado com CachedNetworkImage ou um cache local. Após o primeiro carregamento, os dados são salvos em memória ou SharedPreferences, e o FutureBuilder exibe dados em cache instantaneamente enquanto atualiza da rede em paralelo. Isso melhora a UX através de resposta instantânea.
De acordo com pub.dev (2026), o cache é especialmente relevante para imagens e listas de dados. FutureBuilder com CachedNetworkImageProvider exibe automaticamente uma imagem em cache e, quando ausente — um indicador de carregamento seguido pelo arquivo baixado.
FutureBuilder e gerenciamento manual de estado via setState são duas abordagens para UI assíncrona no Flutter. Cada uma tem suas vantagens e limitações. A escolha depende da complexidade da tela e do número de operações assíncronas.
FutureBuilder ganha em simplicidade: você não precisa declarar campos para estado de carregamento, dados e erro — tudo é gerenciado através do AsyncSnapshot. É ideal para telas simples com uma operação assíncrona (uma requisição HTTP, leitura de banco de dados). No entanto, com 5+ operações assíncronas em uma tela, o FutureBuilder cria aninhamento excessivo — resultando em uma “Pirâmide” de FutureBuilders aninhados.
setState com flags de estado manuais dá mais controle e legibilidade para lógica complexa. Para telas com múltiplas requisições dependentes (carregar usuário → carregar seus pedidos → carregar detalhes do pedido), é melhor usar setState com ChangeNotifier ou Bloc. De acordo com o Guia de Gerenciamento de Estado do Flutter (2026), para cenários complexos, Riverpod ou Bloc são recomendados em vez de FutureBuilder, pois fornecem melhor separação de lógica e apresentação.
Vamos considerar um exemplo prático de FutureBuilder para carregar uma lista de usuários de uma API REST. O código demonstra o tratamento correto dos três estados do AsyncSnapshot: carregamento, erro e dados prontos.
class UserListPage extends StatefulWidget {
const UserListPage({super.key});
@override
State<UserListPage> createState() => _UserListPageState();
}
class _UserListPageState extends State<UserListPage> {
final Future<List<User>> usersFuture = UserRepository().fetchUsers();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Usuários')),
body: FutureBuilder<List<User>>(
future: usersFuture,
builder: (context, AsyncSnapshot<List<User>> snapshot) {
if (snapshot.hasError) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.error_outline, size: 48, color: Colors.red),
const SizedBox(height: 16),
Text('Erro: ${snapshot.error}'),
],
),
);
}
if (snapshot.hasData) {
final users = snapshot.data!;
return ListView.builder(
itemCount: users.length,
itemBuilder: (context, index) {
return ListTile(
leading: CircleAvatar(backgroundImage: NetworkImage(users[index].avatarUrl)),
title: Text(users[index].name),
subtitle: Text(users[index].email),
);
},
);
}
return const Center(child: CircularProgressIndicator());
},
),
);
}
}
No exemplo, FutureBuilder trata todos os três estados. Em erro, um ícone com mensagem de erro é exibido. Em carregamento bem-sucedido — um ListView com avatares e nomes. Durante o carregamento — um CircularProgressIndicator. O Future é declarado como um campo de classe, o que evita invocação repetida em reconstruções. Este padrão cobre 90% dos cenários de uso do FutureBuilder em aplicativos móveis.
Perguntas Frequentes
FutureBuilder chama o builder em cada mudança de estado do Future: a primeira vez na criação (connectionState: none ou waiting), a segunda vez na conclusão (connectionState: done). Se o widget pai for reconstruído, o FutureBuilder também é reconstruído. Para evitar chamadas repetidas, certifique-se de que o Future seja criado fora do método build — caso contrário, cada chamada de build criará um novo Future.
Armazene o Future em um campo do StatefulWidget (no initState) ou use memorização. Se o Future for criado dentro do método build, cada chamada de build criará um novo Future, e o FutureBuilder reiniciará a operação assíncrona. Para StatelessWidget, use o pacote cached_future ou widgets keep-alive para que o Future seja executado uma vez independentemente das reconstruções.
FutureBuilder é projetado para operações assíncronas únicas (uma requisição HTTP, uma leitura de banco de dados). StreamBuilder trabalha com fluxos de dados que podem emitir múltiplos valores ao longo do tempo (chat, atualizações de preço, geolocalização). StreamBuilder suporta ConnectionState.active para dados parciais, enquanto FutureBuilder só suporta waiting e done.
Para múltiplos Futures paralelos, use Future.wait e passe o resultado para um único FutureBuilder. Future.wait recebe uma lista de Futures e retorna um Future — quando todos os Futures concluírem, o builder recebe um array de resultados. Para requisições sequenciais, use uma cadeia de Future.then dentro de um Future ou FutureBuilders aninhados (menos legível). Uma alternativa é o pacote riverpod com AsyncValue para múltiplos estados assíncronos.
FutureBuilder não cancela o Future automaticamente. Para cancelar, use CancelableOperation do pacote async ou um mecanismo personalizado via uma flag cancelled no State. Defina a flag no dispose() e verifique-a após o Future completar antes de chamar setState. Alternativamente, use o pacote riverpod com AutoDispose, que cancela automaticamente operações assíncronas ao sair da tela.
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