Start Debugging

riverpod vs flutter_riverpod vs hooks_riverpod: qual pacote eu realmente preciso?

Instale flutter_riverpod para quase todo app Flutter. Use riverpod apenas para código Dart puro, e hooks_riverpod apenas se você já usa flutter_hooks.

Se o pub.dev está te mostrando riverpod, flutter_riverpod e hooks_riverpod e você não consegue decidir qual adicionar, a resposta para quase todo app Flutter é flutter_riverpod. Adicione riverpod (sem o prefixo flutter_) apenas quando estiver escrevendo Dart puro sem dependência do Flutter, como uma CLI ou um servidor. Adicione hooks_riverpod apenas se você já usa o pacote flutter_hooks e quer HookConsumerWidget. Esses três não são gerenciadores de estado que competem entre si: são camadas da mesma biblioteca, e escolher o errado significa apenas um import um pouco errado, não uma arquitetura diferente. Todas as versões aqui têm como alvo o Riverpod 3.3.2 (a linha 3.0 saiu em 2025-09-10), Flutter 3.44 e Dart 3.12.

São camadas, não rivais

A confusão vem do pub.dev listá-los lado a lado como se fossem alternativas como Provider e Bloc. Não são. riverpod é o motor central, escrito em Dart puro e sem nenhum import do Flutter. flutter_riverpod pega esse motor e adiciona a cola do Flutter: ProviderScope, ConsumerWidget, Consumer e o WidgetRef no qual você chama ref.watch. hooks_riverpod pega flutter_riverpod e adiciona mais uma coisa por cima: a integração com o pacote independente flutter_hooks, expondo HookConsumerWidget.

Cada pacote reexporta o que está abaixo dele. Quando você adiciona flutter_riverpod, também obtém tudo do riverpod sem listá-lo. Quando você adiciona hooks_riverpod, obtém tudo do flutter_riverpod também. É por isso que você nunca instala mais de um deles por vez, e por isso instalar flutter_riverpod e depois importar de package:riverpod/riverpod.dart é um erro que produz confusos erros de símbolos duplicados.

Matriz de recursos

Recursoriverpod 3.3.2flutter_riverpod 3.3.2hooks_riverpod 3.3.2
Depende do FlutterNãoSimSim
Motor de providers (Provider, Notifier, ref.watch)SimSimSim
Widget ProviderScopeNãoSimSim
ConsumerWidget / ConsumerNãoSimSim
HookConsumerWidget / HookConsumerNãoNãoSim
Requer flutter_hooks ao ladoNãoNãoSim
Reexporta o pacote de baixoriverpodflutter_riverpod
Adequado paraCódigo Dart puroA maioria dos apps FlutterApps Flutter que já usam hooks

O tipo AsyncValue, ref.listen, os modificadores de provider como .autoDispose e o comportamento de retry automático adicionado no 3.0 vivem todos no pacote central riverpod, então cada linha que os tem é idêntica entre os três. As únicas diferenças reais são as classes base de widget e a dependência do Flutter.

Quando instalar flutter_riverpod

Este é o padrão, e cobre a grande maioria dos apps.

A instalação é um único comando:

# Flutter 3.44, flutter_riverpod 3.3.2
flutter pub add flutter_riverpod

Um widget mínimo funcional fica assim:

// Flutter 3.44, Dart 3.12, flutter_riverpod 3.3.2
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

final counterProvider = NotifierProvider<Counter, int>(Counter.new);

class Counter extends Notifier<int> {
  @override
  int build() => 0;
  void increment() => state++;
}

void main() {
  // ProviderScope comes from flutter_riverpod
  runApp(const ProviderScope(child: MyApp()));
}

class CounterView extends ConsumerWidget {
  const CounterView({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider);
    return Text('$count');
  }
}

ProviderScope, ConsumerWidget e WidgetRef são todos fornecidos pelo flutter_riverpod. O NotifierProvider, Notifier e state vêm do motor central que o flutter_riverpod reexporta. Você nunca importa package:riverpod/riverpod.dart diretamente em um app Flutter.

Quando instalar o riverpod puro

Recorra ao pacote riverpod puro apenas quando não houver Flutter no projeto de forma alguma.

# Dart 3.12, riverpod 3.3.2
dart pub add riverpod

Em um contexto só Dart não há árvore de widgets, então em vez de ProviderScope você mesmo constrói um ProviderContainer e lê a partir dele:

// Dart 3.12, riverpod 3.3.2 (no Flutter)
import 'package:riverpod/riverpod.dart';

final greetingProvider = Provider<String>((ref) => 'hello from Dart');

void main() {
  final container = ProviderContainer();
  print(container.read(greetingProvider)); // hello from Dart
  container.dispose();
}

Se o seu projeto tem um pubspec.yaml com flutter: sob dependencies, este quase nunca é o pacote que você quer. Adicionar riverpod puro a um app Flutter e depois se perguntar por que ConsumerWidget e ProviderScope não resolvem é um dos erros de configuração do Riverpod mais comuns.

Quando instalar hooks_riverpod

Instale hooks_riverpod apenas quando você já estiver comprometido com o flutter_hooks e quiser usar hooks dentro do mesmo widget que lê providers.

O fato-chave: flutter_hooks e Riverpod são dois pacotes independentes. flutter_hooks é um port dos hooks do React que gerencia estado local do widget, coisas como um TextEditingController ou um AnimationController limitados a um único widget. Riverpod gerencia estado compartilhado da aplicação. Eles resolvem problemas diferentes, e você pode usar qualquer um sem o outro. hooks_riverpod existe puramente para que um único widget possa fazer as duas coisas sem um conflito de herança de classe.

Esse conflito é real. HookWidget (do flutter_hooks) e ConsumerWidget (do flutter_riverpod) são ambas classes base, e uma classe Dart só pode estender uma superclasse. Você não pode escrever class X extends HookWidget, ConsumerWidget. hooks_riverpod resolve isso entregando HookConsumerWidget, uma única classe base que é as duas ao mesmo tempo:

// Flutter 3.44, hooks_riverpod 3.3.2, flutter_hooks 0.21.2
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

class SearchField extends HookConsumerWidget {
  const SearchField({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // useTextEditingController is a hook: local widget state
    final controller = useTextEditingController();
    // ref.watch is Riverpod: shared app state
    final results = ref.watch(searchResultsProvider);

    return TextField(controller: controller);
  }
}

Duas coisas a observar. Primeira, hooks_riverpod não empacota flutter_hooks, então você precisa adicionar os dois:

# Flutter 3.44
flutter pub add hooks_riverpod
flutter pub add flutter_hooks

Segunda, como hooks_riverpod reexporta flutter_riverpod, você não precisa, e não deve, listar também flutter_riverpod no pubspec.yaml. O único import de hooks_riverpod te dá ProviderScope, ConsumerWidget e HookConsumerWidget todos juntos. Um arquivo que só lê providers ainda pode estender o ConsumerWidget comum; você recorre a HookConsumerWidget apenas nos arquivos específicos que também chamam hooks.

A documentação oficial é direta sobre isso para iniciantes: se você é novo no Riverpod, não comece com hooks. Eles adicionam um segundo modelo mental sobre um que já é pouco familiar. Aprenda flutter_riverpod primeiro, e adote hooks_riverpod depois apenas se você se perceber querendo hooks para estado local. Se você gerencia controllers na mão hoje, a disciplina de descarte em descartar controllers do Flutter para evitar vazamentos de memória é exatamente o boilerplate que os hooks buscam remover, que é o caso honesto para adotá-los.

O pacote de anotações substitui o pacote de runtime?

Uma dúvida frequente em seguida: se eu adicionar riverpod_annotation para o codegen do @riverpod, ainda preciso do flutter_riverpod? Sim. O pacote de anotações fornece apenas o marcador @riverpod e os tipos contra os quais o gerador emite. Ele não contém runtime: nem ProviderScope, nem Notifier, nem ref. Seu app ainda roda sobre um dos três pacotes de runtime, e o código gerado importa dele. Então um app Flutter com codegen depende dos dois, flutter_riverpod (runtime) e riverpod_annotation (anotações), não de um no lugar do outro.

A mesma regra de “um único pacote de runtime” vale nos testes. Um teste de widget que monta um ProviderScope usa flutter_riverpod (via flutter_test), enquanto um teste unitário em Dart puro que sobe um ProviderContainer usa o riverpod puro. Você não adiciona um pacote de teste separado para o Riverpod; o ProviderContainer e os overrides de que você precisa para os testes já vêm dentro do pacote de runtime que você instalou.

O detalhe que de fato derruba as pessoas: os pacotes de codegen versionam de forma diferente

Aqui está a parte que surpreende até usuários experientes do Riverpod na era 3.x. Os pacotes de runtime (riverpod, flutter_riverpod, hooks_riverpod) estão na linha 3.3.x, mas os pacotes de geração de código estão em uma versão maior totalmente diferente:

PacotePapelVersão (2026-07)
flutter_riverpodruntime3.3.2
hooks_riverpodruntime3.3.2
riverpodruntime3.3.2
riverpod_annotationanotações de codegen4.0.3
riverpod_generatorcodegen (dev)4.0.4
riverpod_lintregras de lint (dev)3.x

Se você usa a anotação @riverpod para gerar providers, você instala quatro pacotes, não um. riverpod_annotation é uma dependência normal; riverpod_generator e build_runner são dependências de desenvolvimento:

# Flutter 3.44, Riverpod 3.x
flutter pub add flutter_riverpod riverpod_annotation
flutter pub add dev:riverpod_generator dev:build_runner
flutter pub add dev:custom_lint dev:riverpod_lint   # optional, for lint rules

Depois gere com:

# runs the generator once, or use `watch` to keep it running
dart run build_runner watch -d

Não tente fixar riverpod_annotation em ^3.0.0 para casar com o runtime. A linha 4.x de anotações é a que casa com o runtime 3.3.x; os números de versão estão deliberadamente desacoplados porque o gerador evolui no seu próprio ritmo. Deixe o flutter pub add resolver as restrições e não as edite na mão para “alinhá-las”, porque elas não deveriam se alinhar. Esta é a falha de pub get mais comum em um projeto Riverpod 3 recém-criado.

A geração de código é opcional. Tudo neste artigo funciona sem ela. A abordagem de anotações principalmente te poupa de escrever na mão o boilerplate de tipos de provider (NotifierProvider<Counter, int>), e é um bom padrão para projetos novos, mas é uma decisão separada de qual pacote de runtime você instala.

O que digitar de fato

Tirando a explicação, a decisão é curta:

Qualquer que seja o pacote de runtime que você escolher, os providers, a API do Notifier e o AsyncValue se comportam de forma idêntica, porque todos vêm do mesmo motor central. Você está apenas escolhendo quanta cola do Flutter e suporte a hooks empilhar por cima. Uma vez resolvido isso, o aprendizado de verdade está na API em si: como o AsyncValue do Riverpod se compara ao FutureBuilder e ao StreamBuilder, como verificar ref.mounted após um gap async, e como o novo retry automático de providers no 3.0 muda o tratamento de erros. Se você ainda está decidindo se vai usar Riverpod, a comparação Provider vs Riverpod vs Bloc toma essa decisão; se você está saindo da linha antiga, o guia de migração do Riverpod 2.x para o 3.0 cobre as mudanças que quebram.

Fontes

Comments

Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.

< Voltar