En algún momento, todo proyecto de desarrollo debe implementar la persistencia de datos, que se define como la capacidad de un sistema informático (aplicación) de poder conservar los datos aun cuando la aplicación termine su ejecución. En otras palabras, los datos o la información con la que opera permanecen incluso cuando la aplicación no está en ejecución.
En las aplicaciones móviles, generalmente, se utiliza un acceso a una base de datos local, o bien, a un almacenamiento remoto, usualmente en la nube, con el fin de almacenar datos importantes para la operación de la aplicación.
Como futuro desarrollador de aplicaciones móviles, aplicarás los fundamentos de la programación con persistencia de datos en Flutter, partiendo del modelo BLoC, reconociendo cómo hacer un modelado de datos, entendiendo el uso de las API, pasando por Firebase, que es un servicio en la nube de Google para gestionar y almacenar datos, y finalizar con el concepto de una aplicación CRUD.
BLoC (Business Logic Component), o modelo controlador de negocio, es una solución para un modelo de arquitectura de diseño de aplicaciones móviles. Este modelo fue creado por Google y Flutter lo ha adoptado como parte de su filosofía de diseño.
Con base en Vega (2019), BLoC es un modelo de arquitectura que ayuda en el manejo del estado y al acceso de datos desde un lugar principal en el proyecto de desarrollo. Se le puede relacionar con la arquitectura MVVM, lo único que cambia es que el componente ViewModel cambiará por BLoC.
Básicamente, para implementar el modelo de arquitectura BLoC se hace mediante la creación de un archivo con terminación .dart en el proyecto donde residirá todo el código para cumplir la función de hacer las consultas hacia los datos. En otras palabras, los datos fluirán del BLoC a la interfaz de usuario (UI) o viceversa (del UI al BLoC); esto se hace en forma de flujos que en Flutter reciben el nombre de streams.
Modelo para datos
Para realizar peticiones de datos a Internet actualiza los registros en una base local, o bien, elimina un registro en una base de datos en la nube. Comúnmente, se usan datos intermedios como puente entre la aplicación y el recurso de almacenamiento. Los datos recibidos generalmente agrupados como un JSON, en el caso de las llamadas a las API, se deben convertir a un objeto personalizado en Dart; ese proceso se conoce como modelo de datos.
Toma como ejemplo la siguiente situación:
Obtendrás datos de una API que regresará tres datos que corresponden a los datos de álbumes de música donde existirán un identificador de usuario (userId), un identificador de álbum (id) y el título del álbum (title). Tomarás los datos de la consulta mediante un modelo, el cual se inicia creando una clase con los atributos requeridos y una colección de tipo mapa, donde las llaves serán las etiquetas de la API y el contenido de la llave los registros que se obtendrán.
Ejemplo del código:
Esta pantalla se obtuvo directamente del software que se está explicando en la computadora, para fines educativos.
La página quicktype.io es un recurso esencial para crear modelos de datos de conversión para datos de tipo JSON.
El modelo de datos es un recurso muy importante para hacer las interacciones con API, archivos almacenados físicamente en el dispositivo, base de datos, etc. Es importante que, como programador principiante, experimentes y, a través de la experiencia, vayas construyendo los métodos más eficaces para desarrollar los modelos.
API REST
Una API de transferencia de estado representacional (REST) o API de RESTful, o simplemente API REST, es un intermediario entre las aplicaciones y un servicio en la nube. API son las siglas en inglés de interfaz de programación de aplicaciones (API o API web). La propuesta del REST fue desarrollada por Roy Fielding, la cual se ajusta a la arquitectura REST y permite la interacción con los servicios web de RESTful, es decir, los servicios que tienen la arquitectura REST habilitada por completo.
Una API es un conjunto de métodos y protocolos que se utiliza para integrar los desarrollos de software a un servicio de terceros. Es una especie de acuerdo entre el proveedor de información, la aplicación y el usuario que interactúa. La solicitud de información recibe el nombre de llamada, y al contenido devuelto por el proveedor de servicio se le llama respuesta (response).
Por ejemplo, el diseño de una API para un servicio de criptomonedas podría solicitar que el usuario escriba un tipo de criptomoneda y que el productor dé una respuesta en dos partes: la primera sería la tasa de cambio y el ranking global de la criptomoneda.
De acuerdo con la empresa Red Hat (2020), una API REST es una interfaz de programación de aplicaciones (API) que trabaja bajo la arquitectura REST, la cual opera cuando una aplicación cliente envía una solicitud a través de la API. La solicitud se transfiere una representación del estado del recurso requerido. La información es entregada por medio de protocolo HTTP en uno de los siguientes formatos: JSON (JavaScript Object Notation), HTML, XLT, Python, PHP, o bien, texto sin formato.
En el caso de Flutter, para hacer uso de las diferentes API REST, opera con los datos tipo JSON, que es el formato más popular, ya que tanto las máquinas como los humanos pueden entenderlo, y no está ligado a un lenguaje de programación en particular.
En otras palabras, si se busca comunicar un desarrollo de software, un sistema para consultar datos o ejecutar una función en específico, el uso de las API facilita hacer la solicitud y que se ejecute al sistema. Se puede hacer la analogía de una API como si fuera un puente entre los usuarios o la aplicación, y los recursos o servicios en la nube.
Gracias a las API, las organizaciones pueden compartir recursos y las empresas prestadoras del servicio de la API administrarían los recursos necesarios para mantener la seguridad, el control y la autenticación, lo cual facilita y hace los recursos más eficientes. De igual modo, el hecho de que las API manejen las rutinas de autenticaciones de usuarios genera confianza en que la información se segmentará al usuario adecuado.
De acuerdo con la documentación oficial en Flutter, para hacer solicitudes a una API REST se necesita instalar un paquete. El paquete necesario es el llamado http. Para instalar un paquete en Flutter sigue esta secuencia (Flutter, s.f.):
En los archivos de proyectos de Flutter existe un archivo llamado pubspec.yaml, dentro del contenido de ese archivo añade, debajo de la etiqueta “dependencies”, la línea de código que declara el paquete http:
Esta pantalla se obtuvo directamente del software que se está explicando en la computadora, para fines educativos.
Una vez agregada la dependencia del paquete en el archivo de pubspec.yaml, procede a importar en el archivo de código, donde se gestionen las llamadas a la API. Para ello, agrega la siguiente línea de código en la parte superior:
Esta pantalla se obtuvo directamente del software que se está explicando en la computadora, para fines educativos.
Una vez hecho eso, es importante que agregues el permiso de uso de Internet en el manifiesto de Android (AndroidManifiest.xml). El archivo se encuentra en la carpeta Android de la carpeta del proyecto de Flutter.
Esta pantalla se obtuvo directamente del software que se está explicando en la computadora, para fines educativos.
Al realizar estas solicitudes a la API se está realizando una llamada a la red. Entonces, debes tener en cuenta que no obtendrás una respuesta inmediata. Cuando se llama a la API se tomará algo de tiempo en obtener una respuesta por parte del servidor.
Ante esta situación, Dart permite crear una solicitud futura dando la concesión a Flutter para continuar con el trabajo de otros procesos. Para ello se implementa el tipo de dato Future. Future es, por lo tanto, una función que permitirá regresar datos en un futuro próximo.
Para entender esto, piensa en la analogía de un martes de tacos. El Future será el taquero favorito, a él se le puede pedir una orden de tacos. En lo que esta orden es servida, el comensal puede realizar otras tareas como hablar con sus acompañantes, revisar su celular, ver el partido en la televisión, etcétera. El comensal tendrá la certeza de que, sin importar lo que él haga en la espera de su orden, el taquero servirá sus tacos en un futuro próximo. De igual modo, los Futures terminarán de realizar su ejecución.
Por definición, un Future se utiliza para representar un posible valor o error que estará disponible en algún momento en el futuro. Como la respuesta estará disponible en algún momento, al activar el Future puedes liberar recursos para que la aplicación pueda estar activa en la espera y realizar otros procesos.
Tomando el ejemplo del modelo de datos para hacer solicitudes a una API de álbumes de música, aplica un Future con la función de http.Response para obtener los datos.
Esta pantalla se obtuvo directamente del software que se está explicando en la computadora, para fines educativos.
Toma en cuenta que las solicitudes http hacen una llamada de red y se necesitará una manera asíncrona de solicitar información a la API. Por este motivo se necesitan las palabras reservadas async y await.
En palabras simples, async es una palabra reservada que hace que los métodos sean asíncronos. En una función declarada async, cuando se encuentra con await, la siguiente expresión es evaluada y la función ejecutada en el momento es suspendida hasta que se obtenga el resultado.
Al obtener los datos de la API es importante convertir la respuesta http (datos en JSON) al modelo (clase Album). Para ello, utiliza algunos métodos del paquete dart: convert. Si el servidor regresa una respuesta satisfactoria (200), el método fromJSON() del constructor factory se encargará de hacer la conversión de los datos. En caso contrario, si hubiera algún fallo, es importante no regresar un valor nulo, ya que puede ocasionar un error.
El ejemplo de lo mencionado anteriormente se presenta a continuación:
Esta pantalla se obtuvo directamente del software que se está explicando en la computadora, para fines educativos.
Se utiliza un FutureBuilder para obtener los datos y mostrarlos en pantalla, el cual es un método que viene con Flutter y está precisamente destinado a trabajar con consultas asíncronas.
Esta pantalla se obtuvo directamente del software que se está explicando en la computadora, para fines educativos.
Ahora usará la herencia de Dart; para ello se coloca el método en el widget padre. Para obtener los datos, se guarda el resultado y se pasa al widget FutureBuilder.
Esta pantalla se obtuvo directamente del software que se está explicando en la computadora, para fines educativos.
Para hacer el llamado al método de obtención de datos se debe hacer en el ciclo de vida de un StatefulWidget y se implementa en los métodos initState. La información solamente se cargará al inicio, cuando se ejecuta por primera vez el StatefulWidget, o bien, se puede utilizar el método didChangeDependencies si la opción es llamar la consulta de la API en respuesta a un cambio.
El código ejemplo es el siguiente:
Esta pantalla se obtuvo directamente del software que se está explicando en la computadora, para fines educativos.
Ahora se presenta el código completo de ejemplo para contextualizar lo anterior:
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.
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.
Has visto un ejemplo básico de cómo se pueden obtener datos desde Internet mediante el uso de una API REST, pasando por el modelo de datos para hacer la conversión de la respuesta del servidor de la API a través de un archivo JSON.
Estos temas son muy recurrentes en el uso de aplicaciones móviles, ya que los usuarios actuales requieren compartir información y obtener datos de Internet. Estos conceptos pueden ser desafiantes para el programador principiante, por lo que se sugiere practicar y hacer uso de la documentación oficial de Flutter para que el proceso de aprendizaje sea más amigable.
Firebase como base de datos
Esta pantalla se obtuvo directamente del software que se está explicando en la computadora, para fines educativos.
Firebase es un conjunto de servicios y herramientas cliente-servidor para desarrollo de aplicaciones. Fue creado en el año 2011 por una empresa independiente y en el 2014 fue adquirido por Google (López, 2020).
Una de las ventajas, al usar Firebase, es que no es necesario realizar respaldos, configurar firewalls, aplicar medidas de intrusión o políticas de seguridad tediosas, pagar proveedores de servicios de energía o del servicio de Internet o aplicar mantenimientos a los servidores físicos. Básicamente, se confía en la infraestructura de Google para que se encargue de dichas tareas, dejando que el desarrollador enfoque sus esfuerzos en el diseño y funcionamiento de la aplicación móvil.
Tal como lo menciona Napoli (2020), Firebase es una plataforma con una multitud de productos y servicios que comparten y colaboran en conjunto para encargarse de toda la infraestructura del backend, permitiendo conectar las aplicaciones en iOS, Android o aplicaciones web.
Firebase cuenta con varias herramientas para el desarrollo, almacenamiento de datos, análisis de tráfico web, herramientas de marketing digital, etcétera.
A continuación, se mencionan algunas de las herramientas que se pueden utilizar para el desarrollo de aplicaciones.
Tabla 1. Productos Firebase.
Estas herramientas que provee Firestore son realmente poderosas y lo mejor es que Google ofrece un plan gratuito, lo que te permitirá probar todas las características que ofrece la plataforma. Es importante mencionar que el servicio más utilizado para el desarrollo de aplicaciones móviles es el de Cloud Firestore, servicio que permite a los desarrolladores programar con persistencia de datos, almacenando la información en un modelo NoSQL.
El modelo NoSQL es un modelo diferente al modelo tradicional del sistema de gestión de base de datos relacionales (RDBMS), que se basa en una estructura de tablas con columnas y filas.
Napoli (2020) hace una analogía explicando el sistema de base de datos NoSQL, trasladando una comparativa al modelo relacional. Cabe señalar que esta analogía es meramente ilustrativa y sirve como base para introducir a este tipo de paradigma de base de datos.
Tabla 2. Comparación del modelo tradicional relacional y el modelo NoSQL.
En el servicio Cloud Firestore, una colección puede contener únicamente documentos; un documento es un conjunto de datos con la estructura de llave-valor (key-value) y, opcionalmente, puede apuntar a una subcolección. Los documentos no pueden apuntar a otro documento y siempre deben almacenarse en colecciones.
Entonces, en otras palabras, las colecciones son contenedores de documentos, los cuales, a su vez, son los encargados de almacenar los datos bajo el esquema de llave-valor, similar a los archivos JSON. Sin embargo, los documentos en Firestore pueden soportar otros tipos de datos que los JSON no.
Cada documento está limitado a 1 MB de tamaño. A continuación, se muestra un ejemplo de la estructura de un documento de Cloud Firestore NoSQL.
Tabla 3. Ejemplo de la estructura de un documento de Cloud Firestore NoSQL.
Ahora, el documento en vista JSON se vería así:
Esta pantalla se obtuvo directamente del software que se está explicando en la computadora, para fines educativos.
El modelo NoSQL puede ser un paradigma ligeramente confuso, sobre todo para aquellos desarrolladores acostumbrados al modelo relacional o a las sentencias SQL, pero este modelo de almacenamiento suele ser muy práctico y veloz, además de proveer una mejor compatibilidad con el crecimiento vertical, ya que es un modelo que se está implementando cada vez más en el desarrollo de aplicaciones móviles.
Ejemplos de este tipo de modelos es el Mongo DB, o bien, los servicios de base de datos de Amazon AWS, por citar algunos. Conviene hacer prácticas adicionales para llegar a dominar el modelo de bases de datos NoSQL.
Creación de una aplicación. Crear, recuperar, actualizar y eliminar (CRUD)
Esta pantalla se obtuvo directamente del software que se está explicando en la computadora, para fines educativos.
El término CRUD viene de las siglas create (crear), read (recuperar, leer), update (actualizar) y delete (eliminar), por lo tanto, CRUD es la manera, en el ámbito de la programación, que se utiliza para referirse a las cuatro operaciones básicas que se realizan en una base de datos para asegurar la creación, el mantenimiento y la eliminación de registros.
Cada plataforma tiene sus métodos propios para llevar a efecto la persistencia de datos; sin importar qué plataforma se utiliza, todas mantienen en común las cuatro operaciones básicas.
A continuación, se muestra una tabla que ejemplifica y hace la comparación de estas cuatro operaciones en las tecnologías más utilizadas en el desarrollo de aplicaciones móviles.
Tabla 4. Comparación de las operaciones básicas y las tecnologías en el desarrollo de aplicaciones móviles.
En el caso de Flutter, para poder realizar las operaciones CRUD, basta con integrar un paquete en el archivo pubspec.yaml, actualizar los paquetes y utilizar los métodos de cada uno.
Actualmente, la comunidad de Flutter en el Internet es muy activa y será fácil encontrar la manera de implementar las operaciones. Tal fue el caso del ejemplo anterior, en el uso de las API REST, donde se utilizó el método “GET” para la obtención de datos desde Internet.
Persistencia de datos de manera local
Para el manejo de la persistencia de datos de manera local, es decir, almacenar los datos únicamente en el dispositivo donde se ejecuta la aplicación, hay por lo menos tres alternativas viables. La primera es crear archivos JSON, datos de tipo clave-valor en el disco; la segunda es utilizar archivos de uso común como archivos de texto y, la tercera, implementar el uso de una base de datos.
De acuerdo con la documentación oficial de Flutter, en su Cookbook de persistencia de datos (Flutter, s.f.), la mejor alternativa para manejar grandes cantidades de datos es usando una base de datos. En el modo local, las bases de datos en las operaciones CRUD suelen ser mucho más rápidas.
Flutter hace uso de las bases de datos SQLite a través de un complemento llamado sqflite. A continuación, observa un ejemplo muy básico para almacenar datos en una base de datos local SQLite.
Como primer paso, debes agregar las dependencias del paquete en el archivo pubspec.yaml. Este archivo se encuentra en la carpeta raíz del proyecto Flutter.
Esta pantalla se obtuvo directamente del software que se está explicando en la computadora, para fines educativos.
Después importa las librerías necesarias, para ello, en la cabecera del archivo de código se agregarán las importaciones necesarias de las librerías necesarias:
Esta pantalla se obtuvo directamente del software que se está explicando en la computadora, para fines educativos.
Se creará un modelo de datos para almacenar una tabla de alumnos donde solamente se tendrán tres campos de registro único: la matrícula, el nombre y la calificación de cada alumno. El modelo (clase) de datos quedará como se muestra a continuación:
Ejemplo del código:
Esta pantalla se obtuvo directamente del software que se está explicando en la computadora, para fines educativos.
Para consultar, crear y actualizar registros es necesario abrir la conexión a la base de datos, lo cual involucra dos pasos: definir la ruta de la base de datos junto con la combinación del método join y, una vez declarada la conexión a la base de datos, abrirla con la función openDatabase( ) del paquete de sqflite.
Observa el ejemplo del código:
Esta pantalla se obtuvo directamente del software que se está explicando en la computadora, para fines educativos.
El siguiente paso es crear la tabla de alumnos, donde se almacenarán los tres registros adicionales y matrícula como un número entero. Una buena práctica es considerar la matrícula como una llave primaria, el nombre como un dato String que en SQLite es un tipo TEXT y la calificación como un dato entero.
Ejemplo del código:
Esta pantalla se obtuvo directamente del software que se está explicando en la computadora, para fines educativos.
A continuación, realizarás las operaciones básicas de la base de datos para implementar el modelo CRUD de persistencia de datos.
La primera operación será crear registros nuevos. Para ello, utiliza el método insert() para almacenar un mapa en la tabla alumnos; recuerda que, para las operaciones con datos, los modelos de datos hacen uso de los mapas para manipular los datos.
Ejemplo del código:
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.
Esta pantalla se obtuvo directamente del software que se está explicando en la computadora, para fines educativos.
En la segunda operación (recuperar), para obtener la lista de alumnos para consultar (read) los registros en una base de datos se aplican dos pasos, es decir, ejecutar una consulta y los datos serán devueltos en una colección de datos tipo lista de mapas, y después convertir la lista de mapas en una colección lista de alumnos.
Ejemplo del código:
Esta pantalla se obtuvo directamente del software que se está explicando en la computadora, para fines educativos.
La tercera operación: para actualizar los registros en la base de datos, utiliza el método update() de la librería sqflite, para ello debes convertir el registro alumno en un mapa y usar la cláusula where para asegurarte que se actualizará el registro correcto.
Ejemplo del código:
Esta pantalla se obtuvo directamente del software que se está explicando en la computadora, para fines educativos.
Por último, la cuarta operación de un modelo CRUD es la eliminación de un registro en específico (delete). Para realizar esto de la misma librería (la sqflite), toma el método llamado delete(). Se implementará una función que tome la matrícula en el ejemplo de alumnos. Para eliminar al alumno correcto (el que empate con el número de matrícula), al igual que en el método de actualizar registro, haz uso de la cláusula where para delimitar y eliminar el registro correcto.
Ejemplo del código:
Esta pantalla se obtuvo directamente del software que se está explicando en la computadora, para fines educativos.
En este ejemplo identificaste el uso de las operaciones CRUD, aplicando las operaciones en una base de datos local de tipo SQLite. Aunque el acercamiento fue a nivel muy básico, se establecieron las bases adecuadas para trasladar este conocimiento y desarrollar implementaciones mucho más robustas.
Como se ha sugerido anteriormente, acercarse a la documentación oficial y a la comunidad desarrolladora de Flutter garantiza el dominio de estas competencias.
Has trabajado la base de la programación con persistencia de datos de manera general. Tal como se ha mencionado en todos los sistemas de información y en las aplicaciones móviles, almacenar los datos y asegurar su accesibilidad después de que la aplicación haya terminado su ejecución es un requisito para su correcto funcionamiento. Por otro lado, es importante que, como desarrollador, cuentes con las operaciones CRUD en tu despliegue.
Para cumplir con este requisito se han tratado las formas de cumplir con el almacenamiento, ya sea en la nube, por medio del uso de las API y, en el caso de Flutter, bajo el modelo de API REST, pasando por el conjunto de servicios y herramientas para el desarrollo de aplicaciones móviles que ofrece Google en su plataforma llamada Firebase, así como el paradigma de base de datos NoSQL, hasta llegar a la programación de almacenamiento de bases de datos locales con el entorno de SQLite.
Asegúrate de: