Provider: o que é, o gerenciador de estado de aplicativos Flutter

Autor: IT Sectr Publicado: 2026-02-19 Tempo de leitura: 7 min

Provider — um pacote de gerenciamento de estado para Flutter, criado por Remi Rousselet em 2019 como uma camada sobre o InheritedWidget. O Provider resolve o problema de passar dados para baixo na árvore de widgets sem props drilling: qualquer widget pode acessar o estado via context.read<T>() ou context.watch<T>(). De acordo com o pub.dev, o Provider é o gerenciador de estado mais popular do Flutter com mais de 25 mil likes.

Principais pontos

  • ChangeNotifier — uma classe com notifyListeners para notificar assinantes sobre mudanças
  • Consumer — um widget que reconstrói quando o ChangeNotifier muda
  • MultiProvider — composição de vários Providers sem aninhamento
  • ProxyProvider — um Provider que depende de outro Provider
  • context.read / context.watch — métodos para ler estado sem e com assinatura

O que é Provider?

Provider — um pacote para gerenciamento de estado e injeção de dependência no Flutter, construído sobre o InheritedWidget. O Provider fornece um objeto (estado, serviço, repositório) na árvore de widgets e reconstrói automaticamente a UI quando os dados mudam. Ao contrário do uso direto do InheritedWidget, o Provider elimina todo o boilerplate: não é necessário escrever uma subclasse de InheritedWidget, configurar um método estático of() ou gerenciar o aninhamento.

O Provider é a forma oficialmente recomendada pelo Google para gerenciar estado no Flutter (Flutter Team, 2019-2023). O pacote faz parte do Ecossistema Flutter e é mantido pela equipe do Flutter. Em seu lançamento, o Provider foi proposto como substituto para variáveis globais e InheritedWidget: qualquer objeto é acessível de qualquer lugar sem ser passado pelo construtor.

De acordo com a Pesquisa da Comunidade Flutter 2025, o Provider é usado em 72% dos aplicativos Flutter. As principais razões para sua popularidade são: baixo limiar de entrada, suporte integrado ao ChangeNotifier, compatibilidade com outras arquiteturas (MVVM, BLoC) e ausência de dependências externas.

ChangeNotifier e notifyListeners

ChangeNotifier — uma classe integrada do Flutter que implementa o padrão Listener. O ChangeNotifier notifica os assinantes sobre mudanças chamando notifyListeners(). No contexto do Provider, o ChangeNotifier é a classe principal para o estado: cria-se uma classe que estende ChangeNotifier com campos e métodos que chamam notifyListeners() após alterar os dados.

Dart
class CounterProvider extends ChangeNotifier {
  int _count = 0;
  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();
  }

  void reset() {
    _count = 0;
    notifyListeners();
  }
}

Regras do notifyListeners: chamá-lo após modificar completamente os dados — não no meio do método, mas no final. Se um método realiza várias alterações, chame notifyListeners() uma vez após todas as alterações, não após cada uma. Isso evita múltiplos redesenhos em uma única etapa lógica. Para atualizações em lote, use notifyListeners junto com padrões similares a setState.

Alternativas ao ChangeNotifier: ValueNotifier — para um único valor (bom para primitivos), StateNotifier — do pacote state_notifier (raramente usado sozinho). A maioria das soluções com Provider usa ChangeNotifier devido ao seu suporte integrado e simplicidade.

Consumer e context.watch

Consumer — um widget que assina o ChangeNotifier e reconstrói a cada chamada de notifyListeners(). O Consumer aceita uma função builder com três parâmetros: context, model, child. Child — um widget que não depende do modelo e não é reconstruído pelo Consumer. Isso é uma otimização: se o Consumer contém um widget estático (ícone, texto sem dados), ele é passado via child e não é recriado.

Dart
Consumer<CounterProvider>(
  builder: (context, provider, child) => Column(
    children: [
      child!, // não é reconstruído
      Text('${provider.count}'),
      ElevatedButton(
        onPressed: () => provider.increment(),
        child: Icon(Icons.add),
      ),
    ],
  ),
  child: Text('Contador:'),
)

context.watch — um método de extensão do BuildContext para assinar um Provider. Retorna o modelo e assina o widget atual às suas alterações. context.read — acesso sem assinatura (para manipuladores onPressed, initState e dispose). context.select — assinatura em um campo específico do modelo sem reconstruir quando outros campos mudam. Select é a opção mais eficiente para modelos complexos com 10+ campos.

Quando usar Consumer, watch ou select: Consumer — quando um widget child é necessário para otimização. watch — no método build para leitura simples. select — quando o modelo tem vários campos, mas o widget depende apenas de um. O Provider cancela a assinatura automaticamente quando o widget é destruído, prevenindo vazamentos de memória.

MultiProvider e ProxyProvider

MultiProvider — um widget para registrar vários Providers sem aninhamento. Em vez de uma árvore com 5 níveis de Provider → Provider → Provider, o MultiProvider aceita uma lista de provedores. Cada Provider subsequente pode usar os anteriores através do construtor. O MultiProvider é a forma padrão de organizar o nível raiz de uma aplicação.

Dart
MultiProvider(
  providers: [
    ChangeNotifierProvider(create: (_) => CartProvider()),
    ChangeNotifierProvider(create: (_) => AuthProvider()),
    ProxyProvider<AuthProvider, OrderProvider>(
      update: (_, auth, __) => OrderProvider(auth.userId),
    ),
  ],
  child: MaterialApp(home: HomePage()),
)

ProxyProvider — um Provider que depende de outro Provider. O ProxyProvider obtém valores de outros Providers e os passa para seu objeto. Por exemplo, OrderProvider depende de AuthProvider (precisa de userId). Quando o AuthProvider muda, o ProxyProvider recria automaticamente o OrderProvider com o novo userId. ChangeNotifierProxyProvider — a versão do ProxyProvider para ChangeNotifier.

StreamProvider e FutureProvider: StreamProvider assina um Stream (Firebase, WebSocket) e atualiza o Consumer a cada novo evento. FutureProvider — para inicialização assíncrona: executa um Future, mostra carregamento, então passa o resultado para os widgets. Ambos resolvem tarefas comuns sem gerenciamento manual de assinaturas.

Testando o Provider

O Provider é testado envolvendo o widget em um MultiProvider com valores de teste. Nenhuma API ou banco de dados real é necessário para o teste — o Provider é substituído por um objeto simulado. O pacote provider fornece o ProviderScope para isolar testes — cada teste cria sua própria árvore de Provider independentemente.

Dart
import 'package:flutter_test/flutter_test.dart';

void main() {
  testWidgets('Counter increments on button tap',
    (tester) async {
      await tester.pumpWidget(
        ChangeNotifierProvider(
          create: (_) => CounterProvider(),
          child: CounterScreen(),
        ),
      );
      await tester.tap(find.byKey(Key('increment')));
      await tester.pump();
      expect(find.text('1'), findsOneWidget);
    },
  );
}

MockProvider: para testar widgets com um Provider que depende de uma API, crie uma subclasse simulada ou use mockito / mocktail. O Provider não requer ferramentas especiais de simulação — qualquer objeto que estenda ChangeNotifier pode ser passado via create sem chamar o serviço real. Programe os Providers através de interfaces (classe abstrata) para facilitar a substituição.

Provider: desempenho e limitações

O desempenho do Provider é baseado no InheritedWidget: quando um Provider muda, todos os widgets inscritos via context.watch ou Consumer são reconstruídos. Para evitar redesenhos desnecessários, use context.select (assinatura em um campo específico), Consumer com o parâmetro child e const para widgets estáticos. O Provider não reconstrói ramos não inscritos em alterações.

MétodoAssinaturaReconstruçãoUso
context.watchModelo completoQualquer alteraçãoWidgets simples
ConsumerModelo completoQualquer alteraçãoCom otimização child
context.selectCampo específicoApenas quando o campo mudaModelos complexos
context.readNãoNuncaManipuladores de eventos

Limitações: o Provider não suporta isolamento de lógica de negócios no nível de Evento (como o BLoC). Todas as mudanças ocorrem através de chamadas diretas a métodos do ChangeNotifier, o que pode levar a cadeias de alterações não controladas. Para cenários complexos (múltiplas operações assíncronas, validação complexa), o Provider fica aquém comparado ao BLoC e Riverpod.

Migração do Provider: o Provider pode ser facilmente combinado com outros pacotes. Para migrar para Riverpod, use ChangeNotifierProvider.adaptive — um adaptador que permite usar ChangeNotifiers existentes com Riverpod sem reescrever. Para BLoC — o BlocProvider pode ser colocado dentro de uma árvore Provider, substituindo gradualmente ChangeNotifier por Bloc.

Perguntas frequentes

Qual a diferença entre Provider e BLoC?

Provider — uma camada sobre InheritedWidget para injeção de dependência com ChangeNotifier. BLoC — um padrão arquitetural com Event + Stream para isolamento de lógica. Provider é mais fácil de aprender, BLoC estrutura o código de forma mais rigorosa. Provider é adequado para aplicações pequenas e estado de UI, BLoC para lógica de negócios complexa. De acordo com a Flutter Community 2025, ambos são frequentemente usados juntos no mesmo projeto.

O que é ChangeNotifierProvider?

ChangeNotifierProvider — um tipo de Provider para instâncias de ChangeNotifier. Cria o objeto via create, o fornece aos descendentes e reconstrói o Consumer quando notifyListeners é chamado. O ChangeNotifierProvider chama automaticamente dispose no ChangeNotifier quando removido da árvore. Existem três métodos de criação: ChangeNotifierProvider.value (para um objeto existente), ChangeNotifierProvider (para criação lazy) e ChangeNotifierProvider.create (para criação lazy explícita).

Como evitar reconstruções desnecessárias com Provider?

Use context.select em vez de context.watch — o widget reconstrói apenas quando o campo selecionado muda. Divida ChangeNotifiers grandes em vários pequenos (um modelo — uma responsabilidade). Use Consumer child para partes estáticas. Para listas, use ListView.builder com chaves. O Provider DevTools (Flutter Inspector) mostra quais widgets estão reconstruindo e por quê.

Pode-se usar Provider sem ChangeNotifier?

Sim. Provider (sem ChangeNotifier) — para injetar objetos imutáveis (repositório, cliente de API, configuração). ValueListenableProvider — para ValueNotifier. StreamProvider — para Stream (Firebase, WebSocket). FutureProvider — para Future (carregar configuração na inicialização). ProxyProvider — para Providers que dependem de outros Providers. ChangeNotifier só é necessário para estado mutável com atualização de UI.

O que é ProviderNotFoundException?

ProviderNotFoundException — uma exceção em tempo de execução que ocorre ao tentar obter um Provider que não foi declarado acima na árvore de widgets. Causas comuns: o Provider é declarado abaixo do widget que tenta lê-lo; o Provider é declarado em uma rota e lido em outra; um erro de digitação no tipo. Solução: suba o Provider na árvore ou use MultiProvider no nível do MaterialApp para dependências globais.

Resumo

  • Provider — uma camada sobre InheritedWidget para gerenciamento de estado e DI no Flutter
  • ChangeNotifier — uma classe com notifyListeners para atualizações reativas de UI
  • Consumer e context.watch — assinatura de mudanças com reconstrução automática
  • MultiProvider — composição de provedores sem aninhamento de árvore
  • ProxyProvider — uma cadeia de dependências entre Providers
  • context.select — assinatura direcionada a um campo específico do modelo
  • StreamProvider e FutureProvider — suporte integrado para dados reativos e assíncronos

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