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

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
- Estado Global de Sesión y Configuración: Para proveedores globales (autenticación o tema visual), agregue explícitamente
@Riverpod(keepAlive: true). - Enlaces
ref.keepAlive()Dinámicos: Para retener el estado temporalmente mientras finaliza una operación asíncrona tras salir de la pantalla, usefinal link = ref.keepAlive();y libérelo conlink.close(). - 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]
- Migre los antiguos
StateNotifieraNotifieryAsyncNotifierbasados en clases.- Utilice
riverpod_generatorpara automatizar los parámetrosfamilyy la creación de proveedores.- Comprenda la política
autoDisposepor 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.