Provider: qué es, el gestor de estado de aplicaciones Flutter

Autor: IT Sectr Publicado: 2026-02-19 Tiempo de lectura: 7 min

Provider es un paquete de gestión de estado para Flutter, creado por Remi Rousselet en 2019 como una envoltura sobre InheritedWidget. Provider resuelve el problema de pasar datos hacia abajo por el árbol de widgets sin props drilling: cualquier widget puede acceder al estado mediante context.read<T>() o context.watch<T>(). Según pub.dev, Provider es el gestor de estado de Flutter más popular con más de 25 mil likes.

Puntos clave

  • ChangeNotifier — una clase con notifyListeners para notificar a los suscriptores sobre cambios
  • Consumer — un widget que se reconstruye cuando ChangeNotifier cambia
  • MultiProvider — composición de varios Provider sin anidamiento
  • ProxyProvider — un Provider que depende de otro Provider
  • context.read / context.watch — métodos para leer estado sin y con suscripción

¿Qué es Provider?

Provider es un paquete para la gestión de estado e inyección de dependencias en Flutter, construido sobre InheritedWidget. Provider proporciona un objeto (estado, servicio, repositorio) en el árbol de widgets y reconstruye automáticamente la UI cuando los datos cambian. A diferencia del uso directo de InheritedWidget, Provider elimina todo el boilerplate: no es necesario escribir una subclase de InheritedWidget, configurar un método estático of() ni gestionar el anidamiento.

Provider es la forma recomendada oficialmente por Google para gestionar el estado en Flutter (Flutter Team, 2019-2023). El paquete forma parte del Ecosistema Flutter y es mantenido por el equipo de Flutter. En su lanzamiento, Provider se propuso como reemplazo de las variables globales e InheritedWidget: cualquier objeto es accesible desde cualquier lugar sin pasarlo por el constructor.

Según la Encuesta de la Comunidad Flutter 2025, Provider se utiliza en el 72% de las aplicaciones Flutter. Las principales razones de su popularidad son: umbral de entrada mínimo, soporte integrado de ChangeNotifier, compatibilidad con otras arquitecturas (MVVM, BLoC) y ausencia de dependencias externas.

ChangeNotifier y notifyListeners

ChangeNotifier es una clase integrada de Flutter que implementa el patrón Listener. ChangeNotifier notifica a los suscriptores sobre cambios llamando a notifyListeners(). En el contexto de Provider, ChangeNotifier es la clase principal para el estado: se crea una clase que extiende ChangeNotifier con campos y métodos que llaman a notifyListeners() después de modificar los datos.

Dart
class CounterProvider extends ChangeNotifier {
  int _count = 0;
  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();
  }

  void reset() {
    _count = 0;
    notifyListeners();
  }
}

Reglas de notifyListeners: llamarlo después de modificar completamente los datos, no a mitad del método sino al final. Si un método realiza varios cambios, llama a notifyListeners() una vez después de todos los cambios, no después de cada uno. Esto evita múltiples redibujados en un solo paso lógico. Para actualizaciones por lotes, usa notifyListeners junto con patrones similares a setState.

Alternativas a ChangeNotifier: ValueNotifier — para un solo valor (bueno para primitivos), StateNotifier — del paquete state_notifier (rara vez usado solo). La mayoría de las soluciones con Provider usan ChangeNotifier por su soporte integrado y simplicidad.

Consumer y context.watch

Consumer es un widget que se suscribe a ChangeNotifier y se reconstruye en cada llamada a notifyListeners(). Consumer acepta una función builder con tres parámetros: context, modelo, child. Child es un widget que no depende del modelo y Consumer no lo reconstruye. Esto es una optimización: si Consumer contiene un widget estático (icono, texto sin datos), se pasa a través de child y no se recrea.

Dart
Consumer<CounterProvider>(
  builder: (context, provider, child) => Column(
    children: [
      child!, // no se reconstruye
      Text('${provider.count}'),
      ElevatedButton(
        onPressed: () => provider.increment(),
        child: Icon(Icons.add),
      ),
    ],
  ),
  child: Text('Contador:'),
)

context.watch es un método de extensión de BuildContext para suscribirse a un Provider. Devuelve el modelo y suscribe el widget actual a sus cambios. context.read — acceso sin suscripción (para manejadores onPressed, initState y dispose). context.select — suscripción a un campo específico del modelo sin reconstruir cuando otros campos cambian. Select es la opción más eficiente para modelos complejos con 10+ campos.

Cuándo usar Consumer, watch o select: Consumer — cuando se necesita un widget child para optimización. watch — en el método build para lectura simple. select — cuando el modelo tiene varios campos pero el widget depende solo de uno. Provider se da de baja automáticamente al destruirse el widget, evitando fugas de memoria.

MultiProvider y ProxyProvider

MultiProvider es un widget para registrar varios Provider sin anidamiento. En lugar de un árbol con 5 niveles de Provider → Provider → Provider, MultiProvider acepta una lista de proveedores. Cada Provider subsiguiente puede usar los anteriores mediante el constructor. MultiProvider es la forma estándar de organizar el nivel raíz de una aplicación.

Dart
MultiProvider(
  providers: [
    ChangeNotifierProvider(create: (_) => CartProvider()),
    ChangeNotifierProvider(create: (_) => AuthProvider()),
    ProxyProvider<AuthProvider, OrderProvider>(
      update: (_, auth, __) => OrderProvider(auth.userId),
    ),
  ],
  child: MaterialApp(home: HomePage()),
)

ProxyProvider es un Provider que depende de otro Provider. ProxyProvider obtiene valores de otros Provider y los pasa a su objeto. Por ejemplo, OrderProvider depende de AuthProvider (necesita userId). Cuando AuthProvider cambia, ProxyProvider recrea automáticamente OrderProvider con el nuevo userId. ChangeNotifierProxyProvider es la versión de ProxyProvider para ChangeNotifier.

StreamProvider y FutureProvider: StreamProvider se suscribe a un Stream (Firebase, WebSocket) y actualiza Consumer en cada nuevo evento. FutureProvider — para inicialización asíncrona: ejecuta un Future, muestra carga, luego pasa el resultado a los widgets. Ambos resuelven tareas comunes sin gestión manual de suscripciones.

Pruebas con Provider

Provider se prueba envolviendo el widget en un MultiProvider con valores de prueba. No se necesita una API o base de datos real para las pruebas — el Provider se reemplaza con un objeto simulado. El paquete provider proporciona ProviderScope para el aislamiento de pruebas — cada prueba crea su propio árbol de Provider de forma independiente.

Dart
import 'package:flutter_test/flutter_test.dart';

void main() {
  testWidgets('Counter increments on button tap',
    (tester) async {
      await tester.pumpWidget(
        ChangeNotifierProvider(
          create: (_) => CounterProvider(),
          child: CounterScreen(),
        ),
      );
      await tester.tap(find.byKey(Key('increment')));
      await tester.pump();
      expect(find.text('1'), findsOneWidget);
    },
  );
}

MockProvider: para probar widgets con un Provider que depende de una API, crea una subclase simulada o usa mockito / mocktail. Provider no requiere herramientas de simulación especiales — cualquier objeto que extienda ChangeNotifier puede pasarse mediante create sin llamar al servicio real. Programa los Provider mediante interfaces (clase abstracta) para facilitar la sustitución.

Provider: rendimiento y limitaciones

El rendimiento de Provider se basa en InheritedWidget: cuando un Provider cambia, todos los widgets suscritos mediante context.watch o Consumer se reconstruyen. Para evitar redibujados innecesarios, usa context.select (suscripción a un campo específico), Consumer con el parámetro child y const para widgets estáticos. Provider no reconstruye ramas no suscritas a cambios.

MétodoSuscripciónReconstrucciónUso
context.watchModelo completoCualquier cambioWidgets simples
ConsumerModelo completoCualquier cambioCon optimización child
context.selectCampo específicoSolo cuando el campo cambiaModelos complejos
context.readNoNuncaManejadores de eventos

Limitaciones: Provider no admite el aislamiento de la lógica de negocio a nivel de Evento (como BLoC). Todos los cambios ocurren mediante llamadas directas a métodos de ChangeNotifier, lo que puede provocar cadenas de cambios no controladas. Para escenarios complejos (múltiples operaciones asíncronas, validación compleja), Provider se queda corto frente a BLoC y Riverpod.

Migración desde Provider: Provider se puede combinar fácilmente con otros paquetes. Para migrar a Riverpod, usa ChangeNotifierProvider.adaptive — un adaptador que permite usar ChangeNotifiers existentes con Riverpod sin reescribir. Para BLoC — BlocProvider puede colocarse dentro de un árbol de Provider, reemplazando gradualmente ChangeNotifier por Bloc.

Preguntas frecuentes

¿En qué se diferencia Provider de BLoC?

Provider es una envoltura sobre InheritedWidget para inyección de dependencias con ChangeNotifier. BLoC es un patrón arquitectónico con Event + Stream para el aislamiento de la lógica. Provider es más fácil de aprender, BLoC estructura el código de forma más estricta. Provider es adecuado para aplicaciones pequeñas y estado de UI, BLoC para lógica de negocio compleja. Según Flutter Community 2025, ambos se usan a menudo juntos en el mismo proyecto.

¿Qué es ChangeNotifierProvider?

ChangeNotifierProvider es un tipo de Provider para instancias de ChangeNotifier. Crea el objeto mediante create, lo proporciona a los descendientes y reconstruye Consumer cuando se llama a notifyListeners. ChangeNotifierProvider llama automáticamente a dispose en ChangeNotifier al eliminarlo del árbol. Existen tres métodos de creación: ChangeNotifierProvider.value (para un objeto existente), ChangeNotifierProvider (para creación perezosa) y ChangeNotifierProvider.create (para creación perezosa explícita).

¿Cómo evitar reconstrucciones innecesarias con Provider?

Usa context.select en lugar de context.watch — el widget se reconstruye solo cuando cambia el campo seleccionado. Divide los ChangeNotifiers grandes en varios pequeños (un modelo, una responsabilidad). Usa Consumer child para partes estáticas. Para listas, usa ListView.builder con claves. Provider DevTools (Flutter Inspector) muestra qué widgets se reconstruyen y por qué.

¿Se puede usar Provider sin ChangeNotifier?

Sí. Provider (sin ChangeNotifier) — para inyectar objetos inmutables (repositorio, cliente API, configuración). ValueListenableProvider — para ValueNotifier. StreamProvider — para Stream (Firebase, WebSocket). FutureProvider — para Future (cargar configuración al inicio). ProxyProvider — para Provider que dependen de otros Provider. ChangeNotifier solo es necesario para estado mutable con actualización de UI.

¿Qué es ProviderNotFoundException?

ProviderNotFoundException es una excepción en tiempo de ejecución que ocurre al intentar obtener un Provider que no fue declarado más arriba en el árbol de widgets. Causas comunes: el Provider se declara más abajo que el widget que intenta leerlo; el Provider se declara en una ruta y se lee en otra; un error tipográfico en el tipo. Solución: sube el Provider más arriba en el árbol o usa MultiProvider a nivel de MaterialApp para dependencias globales.

Resumen

  • Provider — una envoltura sobre InheritedWidget para gestión de estado e DI en Flutter
  • ChangeNotifier — una clase con notifyListeners para actualizaciones reactivas de UI
  • Consumer y context.watch — suscripción a cambios con reconstrucción automática
  • MultiProvider — composición de proveedores sin anidamiento de árbol
  • ProxyProvider — una cadena de dependencias entre Provider
  • context.select — suscripción selectiva a un campo específico del modelo
  • StreamProvider y FutureProvider — soporte integrado para datos reactivos y asíncronos

Desarrollaremos una aplicación móvil llave en mano

IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.

Discutir el proyecto

Lea también