Los conceptos de la programación orientada a objetos se deben tener muy claros para programar la interfaz del usuario, así como la funcionalidad.
Es necesario considerar que cada widget es un objeto como tal y, con el fin de realizar la estructura del diseño (layout) y la funcionalidad de la aplicación, se integran para formar árboles de widgets. Estos, a su vez, se integran aplicando los principios de clases y herencia.
De igual modo, en el aspecto funcional del código, es importante recordar el uso de los métodos y funciones, ya que en Flutter es esencial para integrar el desarrollo de aplicaciones móviles.
A continuación, conocerás un breve tratado de la programación orientada a objetos con un enfoque en el paradigma de cómputo móvil. Te servirá como una revisión de los fundamentos ajustados al entorno de desarrollo de Flutter. También abordarás el concepto de estado y cuál es su función dentro del desarrollo de aplicaciones móviles.
Una clase se define como un conjunto de objetos y se forma por métodos, funciones, estructura de datos, colecciones, constantes y variables, los cuales son parte de la agrupación. La importancia de la programación orientada a objetos consiste en abstraer los métodos y los datos comunes para convertirlos en un grupo de objetos y declararlos en una clase.
En otras palabras, es una forma de encapsular la funcionalidad. Una clase equivale a la generalización de un tipo específico de objetos. Una instancia es una especificación de una clase con la inicialización de sus atributos. Las instancias son necesarias para poder utilizarlas en este tipo de programación y Dart es un lenguaje orientado a objetos.
Para declarar una clase se usa la palabra reservada “class”, seguida del nombre que recibirá, después, un par de llaves y dentro la definición de miembros y funciones. Por defecto tiene un constructor vacío; un constructor es una instancia de la clase, cuya funcionalidad es inicializar valores.
Ejemplo sencillo de una clase:
Esta pantalla se obtuvo directamente del software que se está explicando en la computadora, para fines educativos.
Crear instancia de una clase puede utilizar la palabra “var” o el nombre de la clase, seguido del nombre de la instancia y le asignas la llamada del constructor de la clase con el uso opcional de la palabra “new''.
Observa el ejemplo de código:
Esta pantalla se obtuvo directamente del software que se está explicando en la computadora, para fines educativos.
Dart tiene nuevas características que permiten abreviar código. Nota que el acceso a las variables y funciones se puede realizar inmediatamente después del constructor, usando “..”.
Esta pantalla se obtuvo directamente del software que se está explicando en la computadora, para fines educativos.
Se puede declarar una clase en Dart con constructores adicionales para inicializar valores o atributos diferentes. Para hacerlo, debes escribir el nombre del constructor junto con punto y un nombre nuevo a asignar. Observa el ejemplo abajo de la clase Superheroe y su constructor nombrado Superheroe.concapa(…).
Esta pantalla se obtuvo directamente del software que se está explicando en la computadora, para fines educativos.
Dart ofrece declarar el nombre de los miembros utilizando la palabra “this” en los constructores como alternativa, de esta manera se reduce significativamente el código ejemplo:
Esta pantalla se obtuvo directamente del software que se está explicando en la computadora, para fines educativos.
Se puede usar de la siguiente manera para llamarlo:
Esta pantalla se obtuvo directamente del software que se está explicando en la computadora, para fines educativos.
Los miembros privados, es decir, aquellos que solamente serán accesibles, o bien, que trabajan en la clase que se declaró, se declaran con un guion bajo antes del nombre.
Por ejemplo:
Esta pantalla se obtuvo directamente del software que se está explicando en la computadora, para fines educativos.
Con Flutter, una buena técnica para ocupar las clases en tiempo es creando una clase por pantalla de la aplicación en Flutter. Las pantallas de la interfaz del usuario que contendrán la aplicación reciben el nombre de escenas, las cuales se implementan por medio de clases.
El uso de clases es esencial para todo lenguaje de programación y proyectos de desarrollo modernos. Debes recordar cómo usar las clases, ya que es esencial para modular el código de acuerdo con la funcionalidad, o bien, con las escenas de la interfaz de desarrollo.
Objetos
Un objeto es una abstracción genérica de datos y de los procedimientos para manipularlos. Analógicamente, viene como referencia a un objeto del mundo real que tiene propiedades (atributos) y acciones que ejecuta. Al igual que los objetos del mundo real, los objetos en desarrollo de software tienen características y actividades que pueden desempeñar.
En Flutter, el estado es cómo se puede apreciar el objeto, o bien, el lugar donde este se encuentra. Los objetos se determinan a partir de una o más variables, y el comportamiento y la funcionalidad se presentan en el código con el uso de métodos y funciones. Básicamente, cualquier elemento que se crea con el código, ya sea una variable, clase o una instancia de esta, es un objeto.
En Flutter, los objetos más utilizados son los widgets, que, a su vez, pueden convertirse en una clase con métodos y widgets hijos, formando así los árboles de widgets. Hay dos tipos de widgets, los widgets stateless (sin estado), que son aquellos que no cambian durante la ejecución, es decir, son estáticos, y los stateful widgets, que se actualizan, cambian su estado y se redibujan en tiempo de ejecución.
Para comprender cuál es la diferencia de un widget sin estado (stateless) y de un widget con estado (stateful), debes entender lo que es un estado. Un estado o “state”, en Flutter, es el parámetro que se maneja cuando la información de un widget cambia y, por ende, se tiene que renderizar (Payne, 2019).
Un StatelessWidget contiene información y esta no cambia, o bien, no se modifica la forma en que se observa mientras está en ejecución. En cambio, un StatefulWidget, al cambiar, debe actualizarse; en otras palabras, dibujarse de nuevo para mantenerse vigente.
Sintaxis de StatefulWidget:
Esta pantalla se obtuvo directamente del software que se está explicando en la computadora, para fines educativos.
A primera vista, el StatefulWidget parece muy complejo, pero con el tiempo te acostumbrarás a dicho widget. Básicamente, el StatefulWidget está dividido en dos clases, una para el widget y otra para el estado (state) (Napoli, 2020).
Cada vez que se cambia el valor del estado es importante hacer uso de la llamada función setState(), la cual se encarga no solamente de actualizar los valores del estado, sino que es la manera más eficiente de actualizar variables, así como de redibujar todo en el widget, incluyendo subárboles.
Esta pantalla se obtuvo directamente del software que se está explicando en la computadora, para fines educativos.
Las preguntas que podría hacerse todo programador inexperto en Flutter serían las siguientes: ¿qué tipo de widget se debe colocar?, ¿un StatefulWidget o un StatelessWidget?
La respuesta es simple, se sugiere utilizar un StatelessWidget, a menos que sea necesario actualizar la información a presentar o que haya una interacción con el usuario, más allá de solamente presentar una pantalla sin interacción. Es decir, donde haya una entrada de información y, por ende, una actualización de esa escena, será necesario implementar un StatefulWidget.
El uso de stateless y stateful widgets es una perfecta ejemplificación del uso de la programación orientada a objetos.
Métodos y funciones
Bajo el concepto de programación orientada a objetos, los objetos no solamente actúan por sí mismos, sino que establecen un sistema de interrelación entre ellos. Los objetos interactúan enviando mensajes unos a otros, y tras la recepción del mensaje, el objeto receptor actuará.
Los métodos y las funciones son los objetos encargados de determinar la funcionalidad con base en la información recibida del mensaje (argumentos). Las funciones y los métodos son muy similares, ambos objetos tienen la labor primordial de agregar operatividad al código. La principal diferencia es que el método siempre pertenece a una clase.
Las funciones en Dart son objetos, lo que significa que incluso se pueden pasar como parámetros de otras funciones como un objeto de tipo “Function”. Para declarar una función, solamente indica el tipo de dato (opcional); en caso de no colocarlo, la función no devolverá ningún valor. Seguido del nombre de la función se abren paréntesis y dentro de los paréntesis se colocan los argumentos de la función. Paso seguido, se abren llaves ({}) y dentro de ellas se coloca el bloque de código con la funcionalidad a destacar.
Ejemplo de funciones en Dart:
Esta pantalla se obtuvo directamente del software que se está explicando en la computadora, para fines educativos.
Dart tiene la propiedad de abreviar el uso de las funciones para evitar colocar las llaves agrupadoras ({}), así como la palabra reservada “return”. Se utiliza la notación de flecha “=>” similar a como se usa en JavaScript.
A continuación, se muestran los ejemplos anteriores utilizando la manera abreviada:
Esta pantalla se obtuvo directamente del software que se está explicando en la computadora, para fines educativos.
En Dart se pueden utilizar funciones anónimas con la notación de flecha:
Esta pantalla se obtuvo directamente del software que se está explicando en la computadora, para fines educativos.
Los parámetros opcionales posicionados permiten que uno o varios parámetros de una función sean opcionales al momento de ser llamada dicha función. Para indicar los parámetros opcionales posicionados basta con encerrarlos entre corchetes []. Hay que considerar que únicamente se puede usar esta notación con el o los últimos parámetros dentro de la definición de parámetros.
Esta pantalla se obtuvo directamente del software que se está explicando en la computadora, para fines educativos.
Hasta aquí, aprendiste a establecer un recordatorio del uso de funciones aplicando este concepto a algunas características con las que cuenta Dart y que permiten abreviar código, o bien, potencializar la funcionalidad al anidar funciones una tras otra y así hacer más robusto el código.
Herencia
La herencia es una característica de la programación orientada a objetos, en la que una clase permite “heredar” atributos y métodos a otra clase hija para reutilizar código. En Flutter se aplica el mismo concepto, sin embargo, la herencia se enfoca mucho en la capacidad de los widgets de pasar las características a los widgets anidados. Se dice que los widgets raíz pasan las características a los widgets hijos.
De acuerdo con Adamyk (2020), los widgets tienen una estructura jerárquica, lo que en otras palabras se conoce como una estructura tipo árbol. Los widgets pueden contener uno o más widgets dentro de sí mismos. Los widgets que contienen otros widgets se llaman widgets padres, y los que se colocan dentro de un widget padre reciben el nombre de widget hijo.
Analiza el código que se presenta por defecto cuando un nuevo proyecto se crea:
Esta pantalla se obtuvo directamente del software que se está explicando en la computadora, para fines educativos.
Aquí, el widget Scaffold es el padre de los widgets, mientras que el widget Center tiene un hijo, el cual es el widget Center y, este a su vez, tiene dos widgets hijos, los widgets Text.
Los widgets son creados por un método llamado “build”, que tiene un argumento de tipo BuildContext. Este argumento describe la posición del widget en el árbol de widgets. Es una clase que trabaja con la relación padre/hijo. BuildContext es el atributo que maneja la información heredada del widget padre en el contexto padre.
El contexto es un vínculo de la ubicación del widget en todo el árbol de widgets. Existe el método of(), que permite llevar la comunicación entre widgets, ya sea que se localicen por debajo o por arriba del árbol de widgets.
El contexto es una manera de comprender el concepto de ubicación de los widgets dentro del árbol de widgets y esa es la arquitectura en la cual se basa el desarrollo de aplicaciones en Flutter, por lo tanto, al trabajar con árboles de widgets, se está trabajando con la capacidad de heredar atributos a todos los widgets debajo del widget padre, también conocido como widget raíz.
Observa este ejemplo:
Esta pantalla se obtuvo directamente del software que se está explicando en la computadora, para fines educativos.
En este ejemplo, el método .of(context) buscará en todo el árbol de widgets el widget Theme y usará el atributo de color primario para utilizarlo en el widget actual.
La herencia juega un papel indispensable al crear los árboles de widgets, ya que la estructura del widget principal o padre de los demás permitirá heredar a sus hijos características esenciales, colores, temas, el estado, el contexto, etc. Vale la pena que trabajes con este tema para lograr la correcta implementación del árbol de widgets.
Navegación y rutas
Las aplicaciones creadas en Flutter pueden contener más de una pantalla para distribuir y organizar mejor la aplicación. Por ejemplo, una tienda en línea que ofrece varios productos y al pulsar en alguno deba mostrar su información. En Flutter, es necesario saber que a las pantallas y páginas se les conoce como rutas, por ende, se utilizará este término para explicar cómo funciona la navegación.
Como ejemplo de funcionamiento, a continuación se van a construir las pantallas, y para la operatividad de navegación, se implementarán dos rutas sencillas con dos botones:
Esta pantalla se obtuvo directamente del software que se está explicando en la computadora, para fines educativos.
Esta pantalla se obtuvo directamente del software que se está explicando en la computadora, para fines educativos.
Ahora usa el método Navigator.push, el cual agrega un “route” a la pila de rutas administradas por Navigator. Con este método podrás avanzar a la siguiente ruta.
Tomando el ejemplo del código anterior, agrega el método Navigator.push dentro del onPressed del botón:
Esta pantalla se obtuvo directamente del software que se está explicando en la computadora, para fines educativos.
Por medio de este código podrás avanzar a la ruta indicada. Ahora, para poder regresar a la primera ruta, es necesario usar el método Navigator.pop, el cual quitará de la pila a la ruta que estás usando y mostrará la ruta anterior de la cual vino.
Observa el ejemplo de uso del método Navigator.pop en la segunda ruta:
Esta pantalla se obtuvo directamente del software que se está explicando en la computadora, para fines educativos.
Has aprendido cómo navegar a una ruta y regresar, pero debes tener cuidado cuando, en varias ocasiones, sea necesario navegar a la misma ruta desde diferentes partes de la aplicación, ya que, si se usa de esta forma, esto puede duplicar el código. En estos casos, es mejor definir “una ruta con nombre” y usarla para navegar. Las rutas, dentro de Flutter, se consideran una colección de datos de tipo Mapa.
Revisa este ejemplo:
Define la ruta principal o ruta inicial dentro de las propiedades del constructor de MaterialApp, como se muestra a continuación:
Esta pantalla se obtuvo directamente del software que se está explicando en la computadora, para fines educativos.
Una vez que defines las rutas con un nombre, se acceden dando este nombre, por ejemplo:
Esta pantalla se obtuvo directamente del software que se está explicando en la computadora, para fines educativos.
Para retirar esta ruta se llama al método Navigator.pop, como en el primer ejemplo.
La navegación entre escenas, ya sea utilizando directamente el método Navigator.push, o bien, utilizando los mapas de rutas, es una excelente opción para organizar la estructura de la aplicación, al segmentar una aplicación y proveer la interacción adecuada al usuario final.
Es importante sugerirte, como desarrollador, que es una buena práctica crear mapas de rutas en lugar de navegar directamente con la ruta, ya que hacerlo facilitará el código y al momento de descontinuar rutas será mucho más sencillo hacerlo, además de agregar orden y estructura a tu desarrollo. Al cubrir este tema de navegación de rutas y escenas estás listo para crear interfaces de usuario mucho más organizadas y completas.
La programación orientada a objetos es un tema vigente en todo tipo de proyectos de desarrollo, incluyendo, sin duda alguna, las aplicaciones móviles. En Flutter, el paradigma orientado a objetos se enfoca en el uso de los widgets, su creación, su manejo en el árbol de widgets, la comunicación y la relación entre estos. También es importante hacer hincapié en que los widgets son clases y solamente hay dos tipos que se implementan en Flutter: el stateless widget y el stateful widget.
Poner en práctica todas las bondades de la programación orientada a objetos le permitirá al desarrollador modularizar su código y segmentar su funcionalidad, así como reutilizar funciones y métodos al hacer uso de propiedades inherentes a este tipo de programación, como es el uso de la herencia para reducir y reciclar código.
Asegúrate de: