← Flutter & Dart

Estado en Flutter

¿Qué opciones de gestión de estado conoces en Flutter y cuándo usarías cada una?

Ver respuesta — intenta responderla en voz alta primero

Las más comunes son: setState para estado local, Provider para estado compartido sencillo, Riverpod como evolución de Provider más segura y testeable, y Bloc/Cubit para lógica de negocio basada en eventos y estados bien definidos. No hay una "mejor": depende del tamaño del proyecto y de la complejidad del estado.

El "estado" es cualquier dato que cambia y afecta la UI. La pregunta clave es quién necesita ese dato:

  • setState: estado local de un widget (un contador, un toggle). Simple y suficiente para lo que vive en un solo widget.
  • Provider: expone objetos a un subárbol y notifica cambios (usa ChangeNotifier + notifyListeners()). Bueno para estado compartido de apps pequeñas y medianas. Construido sobre InheritedWidget.
  • Riverpod: parecido a Provider pero sin depender del BuildContext para leer, con mejor seguridad de tipos y más fácil de testear. Muy popular en proyectos nuevos.
  • Bloc / Cubit: separa la lógica de la UI mediante eventos que entran y estados que salen. Escala bien en apps grandes y equipos, a costa de más código.

Regla: empieza simple (setState) y sube de nivel solo cuando el estado compartido y la complejidad lo justifiquen.

import 'package:flutter/material.dart';

// Patrón ChangeNotifier (la base de Provider)
class ContadorModel extends ChangeNotifier {
  int _cuenta = 0;
  int get cuenta => _cuenta;

  void incrementar() {
    _cuenta++;
    notifyListeners(); // avisa a los widgets que escuchan
  }
}

// En un proyecto real, con el paquete provider, se usaría así:
//
// ChangeNotifierProvider(
//   create: (_) => ContadorModel(),
//   child: MiApp(),
// );
//
// Y dentro de un widget:
//   final modelo = context.watch<ContadorModel>();
//   Text('${modelo.cuenta}');
//   onPressed: () => context.read<ContadorModel>().incrementar();

Meter toda la app en un solo estado global gigante o, al revés, usar una librería pesada como Bloc para una app de dos pantallas. También es común llamar a notifyListeners() de más (reconstruyendo la UI innecesariamente) o modificar el estado sin notificar (la UI no reacciona). Elige la herramienta según la complejidad real, no por moda.

"Para estado local uso setState. Cuando el estado se comparte entre varios widgets, uso Provider, que se apoya en ChangeNotifier y notifyListeners y por debajo usa InheritedWidget. En proyectos nuevos me gusta Riverpod porque es más seguro en tipos, no depende del context para leer y es más fácil de testear. Y para apps grandes con lógica de negocio compleja, Bloc o Cubit, que separan la UI de la lógica con eventos y estados. Mi criterio es empezar simple y subir de nivel solo cuando la complejidad lo pide."

Reto rápido

En el patrón ChangeNotifier, ¿qué pasa si cambias un valor pero olvidas llamar a notifyListeners()?

Ver respuesta

El valor cambia en memoria, pero los widgets que escuchan no se enteran y no se reconstruyen: la UI se queda mostrando el dato viejo. notifyListeners() es lo que dispara la actualización de todos los oyentes. Es el equivalente conceptual a olvidar el setState.