DEV Community

Pablo L
Pablo L

Posted on • Updated on

Patrón Provider en Flutter

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.

class MyModel extends ChangeNotifier{
  int ncars=0;

  void textChange(String cars){
    ncars=cars.length;
    notifyListeners();
  }
}
Enter fullscreen mode Exit fullscreen mode

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.

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,
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

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.

return ChangeNotifierProvider<MyModel>(
      builder: (context) => MyModel(),
      child: materialApp,
    )
Enter fullscreen mode Exit fullscreen mode

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.

Consumer<MyModel>(
                    builder:(context,m,child){
                      return Text(m.ncars.toString());
                    }
                  ),
Enter fullscreen mode Exit fullscreen mode

El segundo Consumer contiene el TextField y reacciona al

                  Consumer<MyModel>(
                      builder:(context,m,child){
                        return TextField(
                          onChanged: (car){
                            m.textChange(car);
                          }
                        );
                      }
                  )
Enter fullscreen mode Exit fullscreen mode

Latest comments (1)

Collapse
 
israeldev_ profile image
Israel Moreno 💀

Muy buen articulo, muy bien explicado y facil de entender. Lo unico que te recomendaria es que mencionaras que se necesita instalar el paquete provider desde pub. De ahi en mas muchas felicidades. Me fue muy util.