effidevFlutter · Edge de Cloudflare · Optimización de costes en la nube
Español

Guía de Arquitectura de Flutter Riverpod 3.0: Notifier, AsyncNotifier y Patrones AutoDispose

Flutter Riverpod 3.0 Architecture Guide

Guía de Arquitectura de Flutter Riverpod 3.0: Notifier, AsyncNotifier y Patrones AutoDispose

Riverpod, la solución de gestión de estado predominante en Flutter, ha introducido una importante renovación arquitectónica en su versión 3.0. Patrones heredados como StateNotifier, ChangeNotifier y proveedores manuales han sido sustituidos por Notifier / AsyncNotifier basados en clases y generación de código estandarizada con riverpod_generator.

Cabe aclarar que la generación de código con @riverpod ya activaba autoDispose (liberación automática de memoria al desmontar pantallas) por defecto desde Riverpod 2.0. Lo que realmente cambia en 3.0 es que las interfaces de tipos de proveedor, antes fragmentadas en AutoDispose* / Family*, se unifican en Provider/Notifier, simplificando notablemente la API.

En este artículo, exploramos los cambios clave de Riverpod 3.0 y los patrones de producción para implementar Notifier, AsyncNotifier y parámetros family.


1. Comparativa: Riverpod Legacy vs. Estándar Riverpod 3.0

Característica Riverpod Legacy (1.x / 2.x) Estándar Riverpod 3.0
Interfaz de Tipos Dividida en Provider / AutoDisposeProvider / FamilyProvider, etc. Unificada en Provider/Notifier (el autoDispose por defecto ya existía desde 2.0 en la generación de código con @riverpod)
Controladores de Estado StateNotifierProvider / FutureProvider Notifier / AsyncNotifier con anotación @riverpod
Argumentos Family Wrappers .family manuales Parámetros nativos en el método build generado
Manejo del Estado Asíncrono Manejo manual con AsyncValue.when() Helpers de AsyncValue mejorados y protección automática de estado

2. Implementación Práctica de Notifier y AsyncNotifier

1) Estado Síncrono: Implementación de Notifier

import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'counter_provider.g.dart';

// La anotación @riverpod aplica autoDispose por defecto
@riverpod
class CounterNotifier extends _$CounterNotifier {
  @override
  int build() {
    // Estado inicial (se vuelve a evaluar al volver a la pantalla)
    return 0;
  }

  void increment() {
    state = state + 1;
  }

  void decrement() {
    state = state - 1;
  }
}

2) Estado Asíncrono: Implementación de AsyncNotifier y Family

import 'package:riverpod_annotation/riverpod_annotation.dart';
import '../models/user.dart';

part 'user_provider.g.dart';

// Los parámetros family se pasan directamente al método build
@riverpod
class UserDetailNotifier extends _$UserDetailNotifier {
  @override
  Future<User> build(String userId) async {
    // Consulta asíncrona de datos
    return await ref.read(userRepositoryProvider).fetchUser(userId);
  }

  Future<void> updateUserName(String newName) async {
    // Establecer estado de carga
    state = const AsyncLoading();

    // Actualización protegida de estado
    state = await AsyncValue.guard(() async {
      final updatedUser = await ref
          .read(userRepositoryProvider)
          .updateName(arg, newName); // 'arg' accede al parámetro family (userId)
      return updatedUser;
    });
  }
}

3. Consumo de AsyncValue y Manejo de AutoDispose en la Interfaz

En la capa de UI, suscríbase al estado usando ref.watch y gestione la renderización con AsyncValue.when.

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

class UserProfileScreen extends ConsumerWidget {
  final String userId;
  const UserProfileScreen({super.key, required this.userId});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // Suscripción al proveedor con el parámetro family
    final userAsync = ref.watch(userDetailNotifierProvider(userId));

    return Scaffold(
      appBar: AppBar(title: const Text('Perfil de Usuario')),
      body: userAsync.when(
        data: (user) => Column(
          children: [
            Text('Nombre: ${user.name}'),
            ElevatedButton(
              onPressed: () {
                ref
                    .read(userDetailNotifierProvider(userId).notifier)
                    .updateUserName('Nuevo Nombre');
              },
              child: const Text('Actualizar Nombre'),
            ),
          ],
        ),
        loading: () => const Center(child: CircularProgressIndicator()),
        error: (err, stack) => Center(child: Text('Error: $err')),
      ),
    );
  }
}

4. Consideraciones sobre AutoDispose en Producción

  1. Estado Global de Sesión y Configuración: Para proveedores globales (autenticación o tema visual), agregue explícitamente @Riverpod(keepAlive: true).
  2. Enlaces ref.keepAlive() Dinámicos: Para retener el estado temporalmente mientras finaliza una operación asíncrona tras salir de la pantalla, use final link = ref.keepAlive(); y libérelo con link.close().
  3. Ciclo de Liberación: AutoDispose no elimina el estado en el instante en que deja de haber escuchadores activos; espera un período de gracia de un frame y, solo si sigue sin usarse después de ese frame, destruye el estado. Tenga esto en cuenta al diseñar estrategias de caché, por ejemplo para guardado temporal de formularios.

Resumen y Conclusión

Riverpod 3.0 ofrece una arquitectura moderna que elimina código repetitivo y maximiza la seguridad de la memoria.

[!NOTE]

  1. Migre los antiguos StateNotifier a Notifier y AsyncNotifier basados en clases.
  2. Utilice riverpod_generator para automatizar los parámetros family y la creación de proveedores.
  3. Comprenda la política autoDispose por defecto y marque los estados globales con @Riverpod(keepAlive: true).

La adopción de Riverpod 3.0 eleva la seguridad de tipos y la mantenibilidad de sus proyectos en Flutter.