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
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 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.
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 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.
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 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.
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.
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.
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.
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étodo | Suscripción | Reconstrucción | Uso |
|---|---|---|---|
| context.watch | Modelo completo | Cualquier cambio | Widgets simples |
| Consumer | Modelo completo | Cualquier cambio | Con optimización child |
| context.select | Campo específico | Solo cuando el campo cambia | Modelos complejos |
| context.read | No | Nunca | Manejadores 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
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.
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).
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é.
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.
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
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.