Patrón Provider en Flutter
Tags: flutter, provider, pattern
Published: 2020-04-20 23:51:44

**Introducción**

Flutter y Widgets. Widgets y Flutter....como sabéis, la mayoría de los elementos visuales y **no visuales** que componen una aplicación en en Flutter son Widgets.

Efectivamente, el hecho que casi todo en un desarrollo Flutter sea un Widget, es una ventaja en términos de simplicidad sobretodo en los comienzos del desarrollo de una aplicación. 

Normalmente en la ejecución de nuestra aplicación y en ciertos puntos de la misma, cambiaremos el estado a través del método setState y Flutter internamente se ocupa de repintar. Esto funciona así de "de serie" y está bien, nada que objetar...sin embargo, cuando una aplicación crece en tamaño y funcionalidad, la cosa se complica por dos razones.

1- La lógica de negocio puede estar dispersa en un montón de Widgets y difícil de encontrar. Además el código de la lógica suele encontrarse en el propio Widget, por lo que interfaz visual y lógica se mezclan.

2- Cuando cambiamos algo en el estado de un Widget, todo el árbol de Widgets es repintado aunque no sea necesario, lo que en términos de eficiencia penaliza.

Cuando la aplicación es lo suficientemente grande y se complica un poco necesitamos de una arquitectura que evite estos problemas.

**El patrón Provider**

El patrón Provider puede que no sea tan sofisticado como otros patrones como por ejemplo el patrón impulsado por Google en el 2018, el patrón Bloc, pero pata aplicaciones de tamaño pequeño/medio puede ser válido. Cuando mecanizamos el proceso del desarrollo de nuestras aplicaciones se simplifican mucho las cosas aplicando este patrón.

**Elementos del patrón Provider**

ChangeNotifier: La clase o clases que contienen el modelo deben heredar de esta clase. Desde esta clase avisaremos a los 'listeners' cuando el modelo cambie mediante el método 'notifyListeners'

ChangeNotifierProvider: Tiene dos parámetros en su construcción.

- create: en donde instanciaremos el modelo.

- child: Los widgets que cuelguen de aquí, serán serán notificados cuando invoquemos el método 'notifyListeners'.

Consumer: A la hora de recuperar el modelo para su consulta o modificación desde los diferentes Widgets de nuestra aplicación, usaremos el Widget Consumer. Eventualmente podemos usar Provider.of(T).

**Ejemplo Práctico**

Vamos a poner como ejemplo una aplicación muy simple en el mostraremos por pantalla el número de caracteres de un TextField.

Primero creamos el modelo heredando de la clase ChangeNotifier. 

```dart
class MyModel extends ChangeNotifier{
  int ncars=0;

  void textChange(String cars){
    ncars=cars.length;
    notifyListeners();
  }
}
```
Fíjate que hemos creado el método textChange dentro del cual modificamos la variable del modelo *ncars* en función de la longitud de la cadena *cars*. Después de la modificación invocamos al método *notifyListeners* de ChangeNotifier. Al hacer esto, Flutter avisará a los escuchadores de que el modelo ha cambiado.

El resto del código de la aplicación.

```dart
class MyApp extends StatelessWidget {
  @override

  Widget build(BuildContext context) {
    MaterialApp materialApp= MaterialApp(
        home: Material(
            child: Center(
                child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: <Widget>[

                  Consumer<MyModel>(
                    builder:(context,m,child){
                      return Text(m.ncars.toString());
                    }
                  ),
                  Consumer<MyModel>(
                      builder:(context,m,child){
                        return TextField(
                          onChanged: (car){
                            m.textChange(car);
                          }
                        );
                      }
                  )
    return ChangeNotifierProvider<MyModel>(
      builder: (context) => MyModel(),
      child: materialApp,
    );
  }
}
```
Observa que en el Widget *MyApp* sin estado, devolvemos un representante de la clase ChangeNotifierProvider<. En el  parámetro constructor *builder* retornamos un representante de nuestro modelo y en el parámetro child el árbol de Widgets.
```dart
return ChangeNotifierProvider<MyModel>(
      builder: (context) => MyModel(),
      child: materialApp,
    )
```

Si te detienes en los Widgets del tipo Consumer, verás que el primero para presentar el número de caracteres del Texfield. Recuerda que este número de caracteres es la variable ncars de la clase MyModel.

Es importante que en el interior de los Widgets tengamos el mínimo código posible, es mejor situar la lógica de negocio en el interior de los ChangeNotifier cómo es el caso del presente ejemplo o en una clase que haga de wrapper de la lógica. Este patrón no se caracteriza por separar claramente la lógica de negocio del modelo pero si la saca de los Widgets y la ordena.

```dart
Consumer<MyModel>(
                    builder:(context,m,child){
                      return Text(m.ncars.toString());
                    }
                  ),
```
El segundo Consumer contiene el TextField y reacciona al
```dart
                  Consumer<MyModel>(
                      builder:(context,m,child){
                        return TextField(
                          onChanged: (car){
                            m.textChange(car);
                          }
                        );
                      }
                  )
```