Riverpod 3.0: O Guia Essencial para Iniciantes

1. Por que Riverpod? (O Problema do “setState”)

No Flutter, o setState é ótimo para um único widget, mas ele falha conforme seu aplicativo cresce. Ele cria:

  • Prop Drilling: Passar dados por 10 widgets apenas para alcançar aquele que realmente precisa deles.

  • UI Sobrecarregada: O código da sua tela vira uma bagunça de lógica e design misturados.

  • Perda de Lógica: Quando um widget é destruído, o estado dele é perdido.

O Riverpod funciona como uma “Fonte Única da Verdade” (Single Source of Truth). Ele mantém seus dados em uma “nuvem global” segura, na qual os widgets podem se “sintonizar”, independentemente de onde estejam na árvore.

2. O Ponto de Entrada: ProviderScope

Antes de poder usar o Riverpod, você deve envolver seu aplicativo em um ProviderScope. Esta é a “Sala de Máquinas” que armazena o estado de todos os seus providers.

void main() {
  runApp(
    // This makes Riverpod available everywhere in your app
    const ProviderScope(
      child: MyApp(),
    ),
  );
}

3. A Magia das Anotações (@riverpod)

O Riverpod 3.0 se afasta das declarações manuais. Agora, utilizamos a Geração de Código (Code Generation).

  • O Benefício: Isso evita os erros mais comuns dos desenvolvedores (como esquecer de limpar — dispose — um provider).

  • A Regra: Você escreve a lógica e o Riverpod gera o “boilerplate” (código repetitivo) para você. É mais limpo, rápido e seguro.

4. A Caixa de Ferramentas “Ref”: Como falar com os Providers

Guia de Uso do Objeto ref

Ferramenta (Tool) Ação (Action) Melhor Caso de Uso (Best Use Case) Zona de Perigo (The Danger Zone)
ref.watch Observa e Reconstrói (Rebuild) Exibir dados na tela. Nunca use dentro de um onPressed.
ref.read Captura o valor uma única vez Dentro de botões ou callbacks. Nunca use dentro de um método build.
ref.listen Dispare-e-esqueça (Fire-and-forget) Exibir SnackBars ou Diálogos. Não use para construir a interface (UI).
ref.invalidate Reseta um provider “Pull-to-refresh” ou Logouts. Usar dentro do build causa loops infinitos

Dentro do seu widget, você usa um ref (Referência) para interagir com os providers.

5. ConsumerWidget vs. ConsumerStatefulWidget

No Riverpod, não utilizamos o StatelessWidget padrão. Usamos seus “primos mais inteligentes”:

  • ConsumerWidget: Use este em 95% das suas telas. Ele te fornece um WidgetRef diretamente no método build.

  • ConsumerStatefulWidget: Use este apenas se precisar de métodos de ciclo de vida local, como initState() ou dispose() (comum ao usar AnimationControllers).

6. Galeria de Providers (Trechos de Código)

A. Provider Funcional (O Leitor)

Ideal para: Dados estáticos ou leitura única (apenas leitura).

// O Provedor: Apenas um valor constante
@riverpod
String shopName(Ref ref) => "Blue Mountain Coffee";

// Utilização no widget
Widget build(BuildContext context, WidgetRef ref) {
  final name = ref.watch(shopNameProvider); 
  return Text(name); // Displays: Blue Mountain Coffee
}
**Quando usar: Para informações "fixas", como URLs de API, títulos de aplicações ou cores de temas

B. O Notifier Provider

Cenário: Ideal para quando o estado aumenta ou diminui (conforme o estado muda).

// O Provedor: Uma classe que gerencia um valor que muda
@riverpod
class Counter extends _$Counter {
  @override
  int build() => 2; // Comece com 2 pacotes

  void add() => state++; // Increase the number
  void remove() => state--; // Decrease the number
}

// Utilização no widget
Widget build(BuildContext context, WidgetRef ref) {
  final count = ref.watch(counterProvider);

  return Row(
    children: [
      IconButton(onPressed: () => ref.read(counterProvider.notifier).remove(), icon: Icon(Icons.remove)),
      Text("$count Packets"),
      IconButton(onPressed: () => ref.read(counterProvider.notifier).add(), icon: Icon(Icons.add)),
    ],
  );
}
**Quando usar: Para qualquer coisa que o utilizador «alterne», «incremente» ou «edite» (como um carrinho de compras ou modo escuro).

C. O FutureProvider

Cenário: Basicamente para chamadas de API.

// O Provider: Gerencia dados que levam tempo para chegar
@riverpod
Future<List<String>> fetchMenu(FetchMenuRef ref) async {
  await Future.delayed(Duration(seconds: 2)); // Simula a espera pela internet
  return ["Espresso", "Latte", "Cappuccino"];
}

// Uso no Widget
Widget build(BuildContext context, WidgetRef ref) {
  final menuAsync = ref.watch(fetchMenuProvider);

  return menuAsync.when(
    data: (items) => Text("Cardápio de hoje: ${items.join(', ')}"),
    loading: () => CircularProgressIndicator(), // Mostra um carregando enquanto espera
    error: (err, stack) => Text("Não foi possível carregar o cardápio"), // Mostra se a internet falhar
  );
}

D. O StreamProvider

Cenário: Para fluxo contínuo de dados, como rastreamento em mapas e fluxos de dados (Data Flow).

// O Provider: Envia atualizações conforme elas acontecem
@riverpod
Stream<String> orderStatus(OrderStatusRef ref) async* {
  yield "Preparando...";
  await Future.delayed(Duration(seconds: 5));
  yield "Servindo...";
  await Future.delayed(Duration(seconds: 5));
  yield "Pronto para retirar! ☕";
}

// Uso no Widget
Widget build(BuildContext context, WidgetRef ref) {
  final statusAsync = ref.watch(orderStatusProvider);

  return statusAsync.maybeWhen(
    data: (msg) => Text(msg),
    orElse: () => Text("Fazendo pedido..."),
  );
}

Aqui estão as dependências necessárias

dependencies:
  flutter:
    sdk: flutter
  # The core engine
  flutter_riverpod: latest 
  # The annotation tool
  riverpod_annotation: latest

dev_dependencies:
  # The generator that writes the code for you
  riverpod_generator: latest
  # The standard runner for all Dart code generation
  build_runner: latest
  # Optional: Helpful for checking logic in Notifiers
  custom_lint: latest
  riverpod_lint: latest

O Pacote Mágico build_runner: Quando você utiliza @riverpod, você está escrevendo instruções de alto nível. No entanto, o Flutter não entende naturalmente o que @riverpod significa. É aqui que entra o Build Runner.

dart run build_runner build || dart run build_runner watch

Por que estou escrevendo este guia

Como desenvolvedor Flutter, notei algo frustrante: o Riverpod 3.0 avançou tão rápido que a internet não conseguiu acompanhar. Embora a nova abordagem baseada em Geradores seja a maneira mais poderosa de construir apps, a maioria dos tutoriais e artigos ainda está presa ao “jeito antigo”. Tenho visto estagiários e colegas desenvolvedores lutarem para encontrar um caminho claro na documentação da versão 3.0.

Eu quis preencher essa lacuna. Quis pegar os conceitos complexos que aprendi — como o Build Runner, AsyncValue e Notifiers — e simplificá-los em um manual que realmente faça sentido para quem está começando hoje. Isso não é apenas sobre código; é sobre fazer com que o gerenciamento de estado pareça um superpoder, em vez de um fardo.

Please follow and like us:
error0
fb-share-icon
Tweet 20
fb-share-icon20