FutureBuilder — o que é, trabalhando com Future no Flutter

Autor: IT Sectr Publicado: 2026-07-02 Tempo de leitura: 8 min

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 — widget Flutter para construir UI baseada no estado do Future via AsyncSnapshot (none, waiting, active, done)
  • AsyncSnapshot — um objeto contendo o estado atual de uma operação assíncrona: connectionState, data e error
  • builder — uma função de retorno chamada em cada mudança de estado do Future para reconstruir a UI
  • Tratamento de erros — AsyncSnapshot.hasError permite exibir uma UI alternativa em falha de operação assíncrona
  • ConnectionState — um enum com quatro valores: none (sem operação), waiting (aguardando), active (stream), done (concluído)

O que é FutureBuilder no Flutter

FutureBuilder é um widget integrado do Flutter do pacote widgets que recebe um Future e uma função builder. Quando o estado do Future muda (executando, concluído com dados, concluído com erro), o FutureBuilder reconstrói automaticamente a UI chamando o builder com um novo AsyncSnapshot. Isso elimina a necessidade de gerenciar manualmente o estado de carregamento via setState e flags.

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.

Como o FutureBuilder funciona internamente

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: estados e propriedades

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.

PropriedadeTipoDescrição
connectionStateConnectionStateEstado atual da conexão (none, waiting, active, done)
dataT?Dados recebidos do Future (null até a conclusão ou em erro)
errorObject?Objeto de erro se o Future concluiu com uma exceção
hasDatabooltrue se data não é null e connectionState é ConnectionState.done
hasErrorbooltrue se o Future concluiu com erro

ConnectionState: quatro estados de uma operação assíncrona

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.

Padrões de uso do FutureBuilder

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.

Carregar dados na inicialização da tela

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.

Carregar com cache e atualização

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 vs setState: o que escolher

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.

Exemplo de FutureBuilder com carregamento de dados de rede

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.

dart
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

Por que o FutureBuilder chama o builder várias vezes?

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.

Como evitar uma requisição repetida ao reconstruir?

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.

Como o FutureBuilder difere do StreamBuilder?

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.

Como usar FutureBuilder com múltiplos Futures?

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.

Como cancelar um Future ao sair da tela?

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

  • FutureBuilder — widget Flutter para construção declarativa de UI baseada no estado do Future via AsyncSnapshot (waiting, done, error)
  • AsyncSnapshot — contêiner com connectionState, data e error; essencial para o tratamento correto de todos os estados da operação assíncrona
  • builder — callback com três ramos: hasError (mostrar erro), hasData (exibir dados), default (indicador de carregamento)
  • FutureBuilder vs setState — FutureBuilder é mais simples para uma operação, setState com Bloc/Riverpod é melhor para lógica complexa com múltiplas requisições
  • Prevenção de requisições repetidas — Future deve ser um campo do State, não o crie no método build para evitar reinício em cada reconstrução
  • Cancelamento de Future — FutureBuilder não cancela o Future no dispose; use CancelableOperation ou uma flag de cancelamento para evitar setState após a destruição
  • Múltiplos Futures — para requisições paralelas use Future.wait com um único FutureBuilder; para sequenciais — cadeias em um único Future

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.

Discutir o projeto

Leia também