Flutter — El Ecosistema GetX ~ Manejador de Estados

mal2tin
8 min readFeb 4, 2021

Traducción libre al español del Artículo:

https://medium.com/flutter-community/the-flutter-getx-ecosystem-state-management-881c7235511d

Créditos al Autor: Aachman Garg

¡Flutter es increíble! Es, por mucho, la forma más rápida de crear aplicaciones verdaderamente multiplataforma sin comprometer la belleza, el rendimiento y la funcionalidad. Con una experiencia de desarrollo diferente a cualquier otro Framework (SDK en realidad), marca el camino. Sin embargo, no es perfecto. Hay algunas cosas que ralentizan el desarrollo de vez en cuando. Tomemos, por ejemplo, implementar el patrón BLoC o el tiempo consumido por el generador de código en MobX o incluso la sintaxis extra-larga de Navigator y MediaQuery. Todo esto afecta la experiencia de desarrollo general con más líneas de código y menos eficiencia.

GetX!

GetX es un micro-framework que tiene como objetivo proporcionar una experiencia de desarrollo de primer nivel combinada con una sintaxis ordenada y un enfoque simple. Al desarrollar con GetX, todo se siente evidente y práctico. Es una sólida combinación de sencillez y potencia. Es uno de esos raros paquetes que intentan hacer todo y realmente lo hacen. GetX proporciona una combinación de soluciones de administración de estado, inyección de dependencias y administración de rutas que funcionan muy bien juntas. Pero las cosas no terminan aquí, proporciona un montón de utilidades que facilitan la implementación de internacionalización, utilización de temas, validación, etc. Todas estas soluciones y utilidades se empaquetan en contenedores compilados individualmente, lo que nos da la libertad de elegir qué usar y qué no usar, sin comprometer el rendimiento. Sin embargo, una vez que esté usando GetX, es difícil no usar todo lo que este paquete tiene para ofrecer, porque todo funciona perfectamente en conjunto. Y eso es lo que yo llamo el ecosistema GetX.

¿Por qué es tan especial?

En primer lugar, ¡me encanta la sintaxis! Antes de usar GetX, no podía imaginar que cosas como navegar a una ruta pudieran ser tan simples. No necesitamos contexto, constructor ni nada. Simplemente escriba Get.to (SomePage ()) y eso es todo. Y ese es solo un ejemplo.

El enfoque principal está en el rendimiento. Normalmente, tendríamos que elegir qué controladores desechar y desecharlos manualmente. Con GetX, es todo lo contrario. Tenemos que elegir cuál guardar en la memoria ya que se eliminan automáticamente. Ahorra memoria y líneas de código.

¡100% de desacoplamiento! Con GetX, es posible lograrlo. La lógica empresarial está separada de las vistas, e incluso las dependencias se pueden separar mediante algo llamado Bindings.

Simplemente funciona. Trabajar con GetX es una experiencia bastante satisfactoria. Siempre que tengo que implementar una determinada función, siempre hay una forma más sencilla de hacerlo con GetX, sin importar el caso de uso.

Está bien mantenido y actualizado. Puede ser un desafío mantener todos sus paquetes actualizados y sincronizados entre sí. A veces, hay cambios importantes y las cosas pueden ponerse feas. GetX centraliza la mayoría de nuestras necesidades de desarrollo en un solo paquete, por lo que no tenemos que preocuparnos por la compatibilidad y el mantenimiento.

¿Manejador de Estado? ¡Manejador de Estado!

Manjeador de estado (State Management) sigue siendo el tema más candente en la comunidad Flutter. Hay un montón de opciones disponibles y es muy intimidante para un principiante elegir una. Además, todos tienen sus pros y sus contras. Entonces, ¿Cuál es el mejor enfoque? La respuesta es simple: con la que se sienta cómodo. ¿GetX puede ser el indicado? ¡Vamos a ver!

GetMaterialApp

Paso 0: usar GetMaterialApp en lugar de MaterialApp.

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GetMaterialApp(); // en lugar de MaterialApp
}
}

¡Listo! Ahora estás oficialmente en el ecosistema. Nota:GetMaterialApp es opcional si va a usar GetX sólo para la administración de estado, y realmente lo usará para administrar rutas, lo cual cubriremos en artículos futuros.

GetxController

Los controladores son clases en las que va toda nuestra lógica de negocio. Todas las variables y métodos se colocan aquí y se puede acceder a ellos desde la vista. Si bien podemos crear una clase simple para este propósito, GetX proporciona una clase llamada GetxController que extiende DisposableInterface. Esto significa que nuestro controlador se eliminará de la memoria tan pronto como los widgets que lo usan se eliminen de la pila de navegación. No tenemos que desechar nada manualmente y el consumo de memoria se reduce, lo que resulta en un alto rendimiento. GetxController viene con métodos onInit () y onClose () que esencialmente reemplazan los métodos initState () y dispose () del StatefulWidget. Esto nos permite evitar completamente el uso de StatefulWidget y escribir código altamente organizado.

class Controller extends GetxController {

@override
void onInit() { // se llama imediatamente despues de que el widget se encuentre en memoria
fetchApi();
super.onInit();
}
@override
void onReady() { // se llama imediatamente despues de que el widget este renderizado en pantalla
showIntroDialog();
super.onReady();
}
@override
void onClose() { // se llama imediatamente despues de que el widget es borrado de memoria
closeStream();
super.onClose();
}
}

GetBuilder

GetBuilder puede envolver cualquier widget para que este interactúe con los métodos y variables del controlador. Podremos llamar a funciones, escuchar cambios de estado, etc.

Entonces, creemos nuestro primer controlador.

class Controller extends GetxController {  int counter = 0;  void increment() {
counter++;
update(); // mira esto!
}
}

Usamos el método update () dentro de cualquier método en el controlador para que nuestros widgets puedan escuchar los cambios realizados por el método. Si ha utilizado el paquete Provider, es como notifyListeners (). Ahora, para que funcione en el lado de la interfaz de usuario, envolvemos nuestro widget con un GetBuilder.

GetBuilder<Controller>( // especifíca el tipo como Controller
init: Controller(), // inicializa el Controller
builder: (value) => Text(
'${value.counter}', // value es una instancia de Controller
),
),
GetBuilder<Controller>( // no se necesita inicializar el Controller nuevamente, solo mencionar su tipo
builder: (value) => Text(
'${value.counter}', // counter es actualizado cuándo increment() es llamado
),
),

Se debe inicializar el controlador solo la primera vez que se usa GetBuilder. Todos los demás GetBuilders compartirán automáticamente el estado del primero, sin importar dónde se encuentren en la aplicación. GetBuilderesencialmente reemplaza al StatefulWidget. Puede mantener todas las páginas sin estado y ajustar widgets específicos en GetBuilder. Es una buena forma de administrar el estado efímero, mientras se mantiene el código organizado. También obtenemos un control refinado sobre qué widgets actualizar mediante la asignación de ID únicos.

GetBuilder<Controller>(
id: 'aVeryUniqueID', // aquí
init: Controller(),
builder: (value) => Text(
'${value.counter}', // esto será actualizado
),
),
GetBuilder<Controller>(
id: 'someOtherID', // aquí
init: Controller(),
builder: (value) => Text(
'${value.counter}', // esto no será actualizado
),
),
class Controller extends GetxController { int counter = 0; void increment() {
counter++;
update(['aVeryUniqueID']); // y aquí!
}
}

GetX

GetBuilderes rápido y ocupa poco espacio en la memoria, pero no es reactivo. Esto significa que nos perderemos el poder de las transmisiones. Bueno, por esta misma razón, se creó GetX (clase). GetX es muy similar a GetBuilderen términos de sintaxis, pero el enfoque se basa puramente en transmisiones. Vamos a ver cómo funciona:

class Controller extends GetxController {  var counter = 0.obs;  void increment() => counter.value++;}

Agregar .obs a cualquier variable la hará observable. Básicamente, estás convirtiendo el contador en un stream de tipo int. Esto significa que podemos escuchar los cambios realizados desde nuestra vista usando GetX.

GetX<Controller>(
init: Controller(),
builder: (val) => Text(
'${val.counter.value}',
),
),

GetX <Controller> es básicamente un StreamBuilder sin plantilla.

counteres de tipo RxInt y para usarlo realmente, necesitamos agregarle .value. Tenemos que hacer esto con cualquier tipo de variable observable. ¿Qué pasa si queremos que un objeto de clase sea observable? Bueno, es el mismo proceso. En una línea de código, podemos hacer observables todas las variables de la clase. Lo mostraré mientras implemente Obx.

Obx

Este es mi favorito personal. Tiene la sintaxis y la implementación más simple. ¡Todo lo que tenemos que hacer es ajustar el widget en Obx (() ⇒) y listo!

class User {
String name;
User({this.name});
}
class Controller extends GetxController {

var user = User(name: "Aachman").obs; // se declara como cualquier otra variable
void changeName() => user.value.name = "Garg"; // usa .value y accede a cualquier variable de la clase
}Obx(() => Text(
'${controller.user.value.name}'
),
),

La sintaxis es más corta que setState, aunque aquí hay un paso adicional. Necesitamos inicializar el controlador para usar nuestras variables y métodos.

Así es como lo hacemos en GetX:

class PageOne extends StatelessWidget {  Controller controller = Get.put(Controller());
// instead of Controller controller = Controller();
}

¿Qué esta pasando aquí? Poniéndolo de esta manera hace que el controlleresté disponible en todas las rutas secundarias de esta clase. Podremos obtener la misma instancia desde cualquier lugar de la aplicación de forma sencilla. Esto nos ayudará a hacer que varios widgets interactúen con el mismo controlador de forma organizada.

Ahora, para usar la misma instancia en otra clase:

class PageSeven extends StatelessWidget {  Controller controller = Get.find();}

GetX encontrará automáticamente la instancia que usamos anteriormente, sin importar dónde se encuentre en el árbol de widgets. Ahora, si cambiamos el valor de una variable de controlador de PageSeven, también se actualizará en PageOne.

¿Cuál elegir?

GetBuilder

Al mantener estado efímero. Lo que básicamente significa usarlo donde quiera que use setState.

Si el rendimiento es la máxima prioridad. El estado se comparte entre los constructores y no se consume mucha RAM.

Si no desea trabajar con streams.

GetX

Cuando desee el poder de la programación reactiva.

Vuelve a dibujar el widget solo cuando el valor de la variable se cambia realmente. Digamos que el valor de la variable se cambia de «Garg» a «Garg», entonces los widgets no se volverán a dibujar.

Si no desea crear una instancia de controladores.

Obx

Si prefiere una sintaxis simple.

Si va a utilizar con varios controladores en el mismo widget.

Obx no necesita un tipo, por lo que se puede utilizar con cualquier número de controladores. Algo como esto:

Obx(() => Text(
'${firstController.counter.value + thirtySeventhController.counter.value}'
),
),

Cuando use Bindings (lo discutiremos en el próximo artículo), siempre prefiera Obx

MixinBuilder

¡¿Qué?! ¿Hay otro? Sí hay. Como sugiere el nombre, combina dos de los enfoques anteriores, que son GetBuildery Obx. Con MixinBuilder, podemos usar los «Aachman» y «Aachman..obs» en el mismo widget. ¿No es genial? ¿Por qué no se comenta arriba? Bueno, por dos razones:

Es el más pesado de todos los demás enfoques, por lo que afectará el rendimiento en aplicaciones más grandes.

Tiene un caso de uso muy específico y poco común, con el que es posible que nunca nos encontremos. Aún así, si funciona para usted, continúe y utilícelo. ¡Aquí está la sintaxis!

class Controller extends GetxController {

int one = 1; // variable simple
void incrementOne() {
one++;
update();
}
var two = 2.obs; // stream reactivo void incrementTwo() => two.value++;} MixinBuilder(
builder: (controller) => Text(
'${controller.one + controller.two.value}' // esto debería funcionar!
),
),

¿Cuál es mi enfoque?

En su mayor parte, uso Obx con Bindings ya que permite el uso de múltiples controladores. Para widgets muy simples, donde el estado efímero necesita cambiar, uso GetBuilder.

Por ejemplo, si un botón cambia de color cuando se presiona. En este caso, trabajar con streams será una exageración y la elección natural sería setState. Pero nuestro objetivo es evitar el uso de StatefulWidget y mantener la lógica empresarial separada de la vista. GetBuilder es perfecto para tales casos de uso.

¡Eso es todo!

Este fue el enfoque de GetX para la gestión del estado, que creo que es una de las mejores soluciones que existen. Espero que este paquete y este artículo agreguen valor a su experiencia de desarrollo de Flutter.

--

--