Compartir en Twitter
Go to Homepage

GUÍA COMPLETA DEL PATRÓN PROVIDER EN FLUTTER 2025

November 22, 2025

Introducción al Patrón Provider en Flutter

Flutter, el framework de Google para desarrollo multiplataforma, ha transformado la forma en que los desarrolladores crean aplicaciones móviles, web y de escritorio. Uno de los desafíos más comunes en el desarrollo de aplicaciones es la gestión eficiente del estado, que determina cómo los datos fluyen y se actualizan en la interfaz de usuario. El patrón Provider es una solución popular y recomendada por la comunidad de Flutter para abordar este problema. Este tutorial explora en detalle cómo implementar el patrón Provider en Flutter, con ejemplos prácticos y actualizaciones relevantes para el año 2025, asegurando que los desarrolladores puedan crear aplicaciones robustas y escalables.

Provider es una biblioteca que simplifica la gestión del estado al proporcionar un enfoque basado en la inyección de dependencias y la herencia de widgets. A diferencia de otras soluciones como Redux o BLoC, Provider es más ligero y fácil de integrar, lo que lo convierte en una opción ideal tanto para principiantes como para desarrolladores experimentados. En este artículo, cubriremos los conceptos fundamentales, la configuración inicial, los tipos de Providers, y ejemplos prácticos para implementar esta solución en proyectos reales.

¿Qué es el Patrón Provider?

El patrón Provider actúa como un contenedor que permite compartir datos entre diferentes partes de una aplicación Flutter. Utiliza el widget InheritedWidget bajo el capó para propagar información a través del árbol de widgets, lo que facilita el acceso al estado desde cualquier widget hijo. La biblioteca provider (disponible en pub.dev) ofrece una API sencilla para gestionar objetos, notificar cambios y actualizar la interfaz de usuario de manera eficiente.

En 2025, la biblioteca provider sigue siendo una de las opciones más recomendadas debido a su simplicidad y compatibilidad con las últimas versiones de Flutter y Dart. La última versión estable de la biblioteca, al momento de escribir este artículo, es la 6.1.0, que incluye mejoras en el rendimiento y soporte para null safety, un estándar en Dart desde la versión 2.12.

Para comenzar, necesitas agregar la dependencia de provider a tu proyecto. A continuación, se muestra cómo hacerlo:

dependencies:
    provider: ^6.1.0

Ejecuta el siguiente comando para instalar la dependencia:

flutter pub get

Este paso asegura que la biblioteca esté disponible en tu proyecto. Ahora, veamos cómo configurar Provider en una aplicación Flutter.

Configuración Inicial de Provider

Para usar Provider, debes envolver el widget raíz de tu aplicación con un Provider o un MultiProvider. Esto permite que los widgets hijos accedan al estado compartido. El widget MaterialApp es un buen lugar para establecer esta configuración, ya que es el punto de entrada de la mayoría de las aplicaciones Flutter.

Supongamos que quieres gestionar un contador simple en tu aplicación. Primero, crea una clase que represente el estado y extienda ChangeNotifier, una clase proporcionada por el paquete provider que notifica a los widgets cuando el estado cambia.

import 'package:flutter/material.dart';

class CounterModel extends ChangeNotifier {
  int _count = 0;

  int get count => _count;

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

En este ejemplo, CounterModel almacena un contador y proporciona un método increment para actualizar su valor. La llamada a notifyListeners() informa a los widgets dependientes que el estado ha cambiado, lo que desencadena una reconstrucción de la interfaz.

Ahora, configura el Provider en el widget raíz:

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

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => CounterModel(),
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: CounterScreen(),
    );
  }
}

El widget ChangeNotifierProvider crea una instancia de CounterModel y la hace disponible para todos los widgets hijos. Ahora, cualquier widget dentro de MyApp puede acceder al contador.

Accediendo al Estado con Provider

Hay varias formas de acceder al estado gestionado por Provider en un widget. Las más comunes son Provider.of, Consumer y Selector. Cada una tiene casos de uso específicos, y elegir la adecuada depende de los requisitos de tu aplicación.

Usando Provider.of

El método Provider.of es la forma más directa de acceder al estado. Es ideal para casos simples donde no necesitas reconstruir el widget cuando el estado cambia. Por ejemplo:

class CounterScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final counter = Provider.of<CounterModel>(context, listen: false);
    return Scaffold(
      appBar: AppBar(title: Text('Contador con Provider')),
      body: Center(
        child: Text('Contador: ${counter.count}'),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: counter.increment,
        child: Icon(Icons.add),
      ),
    );
  }
}

En este código, Provider.of<CounterModel>(context, listen: false) accede al modelo sin suscribirse a sus cambios. Esto es útil para ejecutar métodos como increment, pero el widget no se reconstruirá automáticamente cuando el contador cambie.

Para que el widget refleje los cambios en el estado, elimina el parámetro listen: false o usa listen: true (el valor predeterminado):

final counter = Provider.of<CounterModel>(context);

Sin embargo, usar Provider.of con listen: true en el método build puede causar reconstrucciones innecesarias si el estado cambia con frecuencia. Para optimizar el rendimiento, considera usar Consumer o Selector.

Usando Consumer

El widget Consumer es una alternativa que limita las reconstrucciones a una parte específica del árbol de widgets. Esto es útil cuando solo una sección de la interfaz necesita actualizarse. Modifiquemos el ejemplo anterior:

class CounterScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Contador con Provider')),
      body: Center(
        child: Consumer<CounterModel>(
          builder: (context, counter, child) {
            return Text('Contador: ${counter.count}');
          },
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => Provider.of<CounterModel>(context, listen: false).increment(),
        child: Icon(Icons.add),
      ),
    );
  }
}

El widget Consumer recibe un builder que se ejecuta cada vez que el modelo notifica un cambio. Solo el widget Text dentro de Consumer se reconstruye, lo que mejora el rendimiento en comparación con usar Provider.of en todo el método build.

Usando Selector

Para un control aún más granular, el widget Selector permite especificar qué parte del estado debe desencadenar una reconstrucción. Esto es ideal para modelos complejos con múltiples propiedades. Por ejemplo, si CounterModel tuviera varias propiedades, pero solo quieres reaccionar a los cambios en count:

class CounterScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Contador con Provider')),
      body: Center(
        child: Selector<CounterModel, int>(
          selector: (context, counter) => counter.count,
          builder: (context, count, child) {
            return Text('Contador: $count');
          },
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => Provider.of<CounterModel>(context, listen: false).increment(),
        child: Icon(Icons.add),
      ),
    );
  }
}

El parámetro selector extrae la propiedad count del modelo, y el builder solo se ejecuta cuando count cambia. Esto reduce las reconstrucciones innecesarias y es especialmente útil en aplicaciones con estados complejos.

Tipos de Providers

La biblioteca provider ofrece varios tipos de Providers para diferentes casos de uso. Además de ChangeNotifierProvider, aquí hay algunos comunes:

Provider

El Provider básico es útil para compartir objetos que no cambian, como configuraciones o servicios. Por ejemplo, para compartir una configuración de tema:

class ThemeConfig {
  final Color primaryColor;

  ThemeConfig(this.primaryColor);
}

void main() {
  runApp(
    Provider(
      create: (context) => ThemeConfig(Colors.blue),
      child: MyApp(),
    ),
  );
}

ValueProvider

ValueProvider es similar a Provider, pero permite proporcionar un valor específico en lugar de una instancia de clase. Es útil para valores simples como cadenas o números:

ValueProvider<String>(
  value: 'Hola, Flutter',
  child: MyApp(),
)

FutureProvider

FutureProvider es ideal para gestionar datos asíncronos, como resultados de una API. Por ejemplo, para cargar datos de una API:

Future<String> fetchData() async {
  await Future.delayed(Duration(seconds: 2));
  return 'Datos cargados';
}

void main() {
  runApp(
    FutureProvider(
      create: (context) => fetchData(),
      initialData: 'Cargando...',
      child: MyApp(),
    ),
  );
}

En un widget hijo, puedes consumir el resultado con Consumer:

Consumer<String>(
  builder: (context, data, child) {
    return Text(data);
  },
)

StreamProvider

StreamProvider maneja flujos de datos, como actualizaciones en tiempo real desde una base de datos como Firebase. Por ejemplo:

Stream<int> countStream() async* {
  int i = 0;
  while (true) {
    await Future.delayed(Duration(seconds: 1));
    yield i++;
  }
}

void main() {
  runApp(
    StreamProvider(
      create: (context) => countStream(),
      initialData: 0,
      child: MyApp(),
    ),
  );
}

Estos tipos de Providers cubren la mayoría de los casos de uso en aplicaciones Flutter, desde estados simples hasta datos asíncronos complejos.

MultiProvider para Estados Complejos

En aplicaciones reales, es común necesitar múltiples Providers para gestionar diferentes aspectos del estado, como autenticación, datos de usuario y configuraciones. El widget MultiProvider permite combinar varios Providers en una sola configuración:

void main() {
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (context) => CounterModel()),
        Provider(create: (context) => ThemeConfig(Colors.blue)),
        FutureProvider(create: (context) => fetchData(), initialData: 'Cargando...'),
      ],
      child: MyApp(),
    ),
  );
}

MultiProvider es eficiente y mantiene el código organizado, especialmente en aplicaciones grandes con múltiples fuentes de estado.

Buenas Prácticas para Usar Provider

Para aprovechar al máximo el patrón Provider, sigue estas recomendaciones:

  1. Usa Consumer o Selector para actualizaciones específicas: Evita depender de Provider.of en el método build para reducir reconstrucciones innecesarias.
  2. Mantén los modelos simples: Los modelos que extienden ChangeNotifier deben centrarse en una única responsabilidad para facilitar el mantenimiento.
  3. Aprovecha null safety: Desde Dart 2.12, null safety es obligatorio. Asegúrate de que tus modelos y Providers manejen correctamente los valores nulos.
  4. Organiza la estructura del proyecto: Coloca los modelos en una carpeta models y los servicios en una carpeta services para mantener el código modular.
  5. Prueba tus Providers: Usa paquetes como mockito para probar cómo se comporta tu aplicación cuando el estado cambia.

Un ejemplo de estructura de directorios para un proyecto que usa Provider podría ser:

lib/
├── models/
│   └── counter_model.dart
├── services/
│   └── api_service.dart
├── screens/
│   └── counter_screen.dart
├── main.dart

Ejemplo Práctico: Lista de Tareas

Para ilustrar cómo Provider se aplica en un escenario real, implementemos una aplicación de lista de tareas. La aplicación permitirá agregar tareas y marcarlas como completadas.

Primero, crea un modelo para gestionar las tareas:

import 'package:flutter/material.dart';

class Task {
  String title;
  bool isCompleted;

  Task({required this.title, this.isCompleted = false});
}

class TaskModel extends ChangeNotifier {
  List<Task> _tasks = [];

  List<Task> get tasks => _tasks;

  void addTask(String title) {
    _tasks.add(Task(title: title));
    notifyListeners();
  }

  void toggleTask(int index) {
    _tasks[index].isCompleted = !_tasks[index].isCompleted;
    notifyListeners();
  }
}

Configura el Provider en el widget raíz:

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => TaskModel(),
      child: MyApp(),
    ),
  );
}

Crea una pantalla para mostrar y gestionar las tareas:

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

class TaskScreen extends StatelessWidget {
  final TextEditingController _controller = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Lista de Tareas')),
      body: Column(
        children: [
          Padding(
            padding: EdgeInsets.all(16.0),
            child: Row(
              children: [
                Expanded(
                  child: TextField(
                    controller: _controller,
                    decoration: InputDecoration(hintText: 'Nueva tarea'),
                  ),
                ),
                IconButton(
                  icon: Icon(Icons.add),
                  onPressed: () {
                    if (_controller.text.isNotEmpty) {
                      Provider.of<TaskModel>(context, listen: false)
                          .addTask(_controller.text);
                      _controller.clear();
                    }
                  },
                ),
              ],
            ),
          ),
          Expanded(
            child: Consumer<TaskModel>(
              builder: (context, taskModel, child) {
                return ListView.builder(
                  itemCount: taskModel.tasks.length,
                  itemBuilder: (context, index) {
                    final task = taskModel.tasks[index];
                    return ListTile(
                      title: Text(
                        task.title,
                        style: TextStyle(
                          decoration: task.isCompleted
                              ? TextDecoration.lineThrough
                              : null,
                        ),
                      ),
                      leading: Checkbox(
                        value: task.isCompleted,
                        onChanged: (value) {
                          taskModel.toggleTask(index);
                        },
                      ),
                    );
                  },
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}

En este ejemplo, TaskModel gestiona una lista de tareas, y la pantalla usa Consumer para actualizar la lista cuando se agregan o modifican tareas. El uso de Provider.of con listen: false en el botón de agregar tareas evita reconstrucciones innecesarias.

Actualizaciones en Provider para 2025

En 2025, la biblioteca provider ha evolucionado para integrarse mejor con las características modernas de Flutter, como el soporte mejorado para Flutter Web y la compatibilidad con la compilación Ahead-of-Time (AOT). Además, la comunidad ha adoptado ampliamente el uso de Selector para optimizar el rendimiento en aplicaciones complejas. Los desarrolladores también están combinando Provider con otras herramientas como Riverpod, una evolución del patrón Provider que ofrece una API más moderna y tipada.

Para mantener tu aplicación actualizada, asegúrate de:

  • Usar la última versión de provider (6.1.0 o superior).
  • Implementar null safety en todos los modelos y servicios.
  • Probar la aplicación en múltiples plataformas (móvil, web, escritorio) para garantizar la compatibilidad.

Conclusiones

El patrón Provider sigue siendo una de las soluciones más efectivas y accesibles para la gestión del estado en Flutter en 2025. Su simplicidad, combinada con su flexibilidad, lo hace adecuado para proyectos de cualquier tamaño, desde aplicaciones simples hasta sistemas complejos con múltiples fuentes de datos. Al dominar conceptos como ChangeNotifierProvider, Consumer y Selector, los desarrolladores pueden crear aplicaciones eficientes y mantenibles.

Este tutorial ha cubierto los fundamentos de Provider, desde la configuración inicial hasta ejemplos prácticos como una lista de tareas. Al seguir las buenas prácticas y aprovechar las actualizaciones más recientes de la biblioteca, puedes construir aplicaciones Flutter que sean rápidas, escalables y fáciles de mantener. Si estás comenzando con Flutter o buscas optimizar tus proyectos existentes, el patrón Provider es una herramienta esencial en tu arsenal de desarrollo.