Desarrollo de aplicación con base de datos SQL en Flutter y publicada en Play Store

Desarrollo de aplicación con base de datos SQL en Flutter y publicada en Play Store

Una forma fácil y práctica de acceder a sistemas de control y bases de datos es mediante el uso de aplicaciones para teléfonos móviles, ya que hoy en día prácticamente todo el mundo lleva una en el bolsillo, ya sea Android o iOS.

Un framework muy popular para desarrollar aplicaciones para múltiples plataformas es Flutter, que usaremos a lo largo de este artículo para crear y publicar una aplicación con una base de datos en Play Store.

Flutter y el lenguaje Dart

Flutter fue un proyecto desarrollado por Google y lanzado en 2017, con el objetivo de ser una alternativa más moderna y eficiente para el desarrollo de aplicaciones, utiliza como base el lenguaje Dart, el cual también fue desarrollado por Google.

Para aquellos que ya conocen lenguajes como Python, JavaScript y C++, no tendrán muchas dificultades con Dart, ya que la sintaxis es similar.

Está orientado a objetos, tiene escritura opcional y un modo de compilación Just-In-Time (JIT), lo que permite al desarrollador una buena flexibilidad en la forma en que escribe el código. A continuación se muestra un ejemplo de creación de funciones y bucles en Dart, respectivamente:

void printMessage(String message) {
    print(message);
}

for (int i = 0; i < 10; i++) {
    print(i);
}


Flutter utiliza mucho el concepto de orientación a objetos de Dart, tiene una arquitectura basada en widgets, que son objetos de la clase widget y representar los elementos que se dibujarán en la pantalla. A continuación se muestra un ejemplo de cómo funcionan las clases y objetos en Dart:

class Pessoa {
    String nome;
    int idade;

    Pessoa(this.nome, this.idade); // Esse é o construtor, ele possui o mesmo nome da classe

    void cumprimentar(){
        print("Prazer, meu nome é $nome e tenho $idade anos");
    }
}

void main() {
    Pessoa joao = Pessoa("João", 15);
    joao.cumprimentar();
}


Para iniciar el proyecto, tenemos que configurar el entorno. Descarga Flutter SDK en el sitio web oficial, luego descarga e instala el Android Studio. En él, abre SDK Manager y descarga Android SDK y Command Line Tools. Es posible que debas agregar la ruta del SDK el PATH del sistema, por lo que una vez que Flutter esté instalado, usa el comando flutter doctor en la terminal para ver si falta algo. Asegúrate de que también esté instalada la versión más actualizada de Java, ya que esto puede generar un error.

Puedes iniciar un dispositivo virtual o utilizar un teléfono celular conectado a la computadora para probar la aplicación. Para ver la lista de dispositivos disponibles, simplemente usa el comando:

sdkmanager --list


Con la lista, puedes intentar crear un emulador con el siguiente comando, simplemente cambiando el nombre del dispositivo y el nombre del paquete a uno de los de la lista:

avdmanager create avd --name Nexus_5X_API_31 --package "system-images;android-31;google_apis;x86_64" --device "Nexus 5X" --tag google_apis --abi x86_64


En caso de error, abre Android Studio, ve a la pestaña del administrador de dispositivos y crea manualmente un nuevo dispositivo, eligiendo su resolución y la versión de Android. Una vez hecho esto, el emulador ya estará disponible para Flutter.

flutter emulators


El comando anterior enumerará los emuladores que están disponibles, obtendrá el nombre del emulador y ejecutará el mismo comando con la opción de inicio seguida del nombre del dispositivo:

flutter emulators --launch Nexus_5X_API_31


La otra forma de probar la aplicación Flutter es con un dispositivo físico. La ventaja de este método es que tu computadora no se ralentizará por ejecutar el emulador. Para hacer esto, necesitas conectar tu teléfono celular a la computadora a través de USB, luego busca la opción en stu teléfono celular para habilitar la depuración a través de USB, que generalmente se encuentra en la configuración del desarrollador. Así, cuando ejecutes flutter doctor, un nuevo dispositivo debería aparecer como disponible.

Con todo configurado, simplemente crea el proyecto usando el comando flutter create, seguido del nombre de tu aplicación:

flutter create task_list


Este comando generará un proyecto Flutter que contiene varias carpetas y archivos que servirán para transformar el código que escribimos en un ejecutable para diferentes plataformas, a saber: Android, iOS, Windows, Linux, Mac y web. Como en este artículo nos centramos en Android, puede eliminar carpetas de otras plataformas. El código en sí está en la carpeta lib en el archivo main.dart.

Al crear el proyecto, el archivo main.dart ya vendrá con un código de ejemplo. Al principio, se importa el paquete flutter/material.dart, que le dará acceso a varios componentes ya creados para agregar a su código, desde elementos simples como texto, hasta elementos de interfaz complejos como PageView que sirve para el layout y para hacer animaciones. Combinando estos componentes en una estructura de árbol, podemos crear una interfaz compleja.

La función main será donde inicia el programa y donde llamaremos a la función flutter runApp, la cual recibirá como argumento un único widget y aumentarlo al tamaño de la pantalla. Independientemente del dispositivo utilizado, este widget que representa la aplicación es una clase que extiende la clase StatelessWidget y recibirá un parámetro opcional que es la clavepara identificar su instancia. StatelessWidget posee una función interna llamada build, que recibirá el contexto como parámetro y devolverá un widget, el cual tendremos que sobreescribir para generar los nuevos widgets:

import "package:flutter/material.dart"

void main(){
    runApp(TaskListApp());
}

class TaskListApp extends StatelessWidget {
    const TaskListApp({Key? key}) : super(key: key);

    @override build(BuildContext context) {
        return MaterialApp(
            home: Scaffold(
                appBar: AppBar(
                    backgroundColor: Colors.red,
                    title: const Text('Task List'),
                ),
            ),
        );
    }
}


El Widget MaterialApp será retornado por el build de la clase TaskListApp y, dentro de él habrá el Scaffold, donde colocaremos todos los otros widgets de nuestra aplicación. Lo más común para la creación del layout de la aplicación son los containers porque reciben un child como argumento y se pueden personalizar cambiando los márgenes, el padding, color, tamaño y más:

Scaffold(
    appBar: AppBar(
        backgroundColor: Colors.red,
        title: const Text('Task List'),
    ),
    body: Container(
        padding: const EdgeInsets.all(10.0),
        margin: const EdgeInsets.all(5.0),
        color: Colors.red,
        width:100,
        height:100,
        child: Text('Tarefa 1'),
    ),
)


Otros widgets más enfocados son Center para centralizar o SizedBox para crear un rectángulo con un tamaño fijo. Para organizar múltiples elementos en filas o columnas, podemos usar Column y Row que, a diferencia de Container, reciben múltiples childs. Como argumento, también podemos, modificando la alineación en el eje principal u otro, cambiar la posición de los elementos en la lista:

body: Row(
    mainAxisAlignment: MainAxisAlignment.spaceEvenly,
    crossAxisAlignment: CrossAxisAlignment.end,
    children: const [
        Icon(Icons.person),
        Icon(Icons.leaderboard),
        Icon(Icons.backpack)
    ],
)


Todos los child, por defecto, tienen el mismo tamaño, aunque es posible dejar más espacio para uno, colocándolos dentro de Expanded y alterando la propiedad flex:

body: Row(
    mainAxisAlignment: MainAxisAlignment.spaceEvenly,
    crossAxisAlignment: CrossAxisAlignment.end,
    children: const [
        Expanded(
            flex: 4,
            child: Icon(Icons.person),
        ),
        Expanded(
            flex: 2,
            child: Icon(Icons.leaderboard),
        ),
        Icon(Icons.backpack)
    ],
)


Si necesitas colocar más elementos dentro del Row o Column, provocando que su tamaño exceda el tamaño de la pantalla del dispositivo, aparecerá un error. Para solucionar este problema, podemos utilizar el ListView en el lugar, que permite hacer el scroll con los elementos:

body: ListView(
    scrollDirection: Axis.vertical,
    children: [
        Container(
            width: 100,
            height: 400,
            color: Colors.yellow,
            child: const Center(child: Icon(Icons.backpack))
        ),
        Container(
            width: 100,
            height: 400,
            color: Colors.green,
            child: const Center(child: Icon(Icons.leaderboard))
        ),
        Container(
            width: 100,
            height: 400,
            color: Colors.red,
            child: const Center(child: Icon(Icons.person)),
        )
    ]
),


Otra ventaja de utilizar ListView es que posee un builder, una función que te permite crear elementos de forma dinámica. Con esto, puedes crear arrays de strings, números y hasta widgets para colocarlos dentro de ListView:

List<String> lista_tarefas = ['Fazer a feira', 'Lavar a louça', 'Pagar o boleto']

body: ListView.builder(
    scrollDirection: Axis.vertical,
    itemCount: 5,
    itemBuilder: (BuildContext context, int index) {
        return Container(
            width: 100,
            height: 100,
            color: Colors.red,
            child: Center(child: Text(lista_tarefas[index])),
        );
    }
)


El itemCount va a determinar cuántos elementos van a quedar dentro de ListView, mientras que itemBuilder es una función que retornará un elemento para cada existente en itemCount. En este caso, tenemos un contenedor con texto centrado. Fíjate que el texto proviene de la lista, accediendo por el parámetro index, el contador de itemBuilder.

La clase StatelessWidget no tiene estado, lo que significa que los datos dentro de ella no se alteran. Sin embargo, existe otra clase donde podemos alterar variables dinámicamente, StatefullWidget. Se separa en dos clases: la primera mantiene la widget propriamente con inmutable, al tiempo que la segunda es el estado del widget. En esta segunda clase, podemos definir variables que pueden ser alteradas con la función nativa setState:

class ListaTarefas extends StatefulWidget {
  const ListaTarefas({Key? key}) : super(key: key);

  @override
  State<ListaTarefas> createState() => _ListaTarefasState();
}

class _ListaTarefasState extends State<ListaTarefas> {

  List<String> tasks = [‘Tarefa 1’, 'Tarefa 2', 'Tarefa 3', 'Tarefa 4', 'Tarefa 5'];
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Container(
          height: 50,
          child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              const Text('Data'),
              FloatingActionButton(
                child: const Icon(Icons.add),
                onPressed: () {
                  setState(() {
                    tasks.add('value');
                  });
                }
              )
            ],
          ),
        ),
        Expanded(
          child: ListView.builder(
                scrollDirection: Axis.vertical,
                itemCount: teste.length,
                itemBuilder: (BuildContext context, int index){
                  return Container(
                    width: 100,
                    height: 100,
                    color: Colors.red,
                    child: Center(child: Text(tasks[index])),
                  );
                },
          ),
        ),
      ],
    );
  }
}



Base de datos SQL con Flutter

A veces, tu aplicación necesitará guardar datos en el dispositivo; si está estructurada, utilizará una base de datos SQL y la plataforma SQL que está disponible en casi todos los dispositivos hoy en día, incluidos los teléfonos inteligentes. SQLite es pequeño y utiliza un solo archivo para almacenar tus datos. Flutter tiene varias formas de desarrollar aplicaciones con bases de datos SQL, siendo una de las bibliotecas más utilizadas sqflite.

Para agregar la biblioteca, abre el archivo pubspec.yaml y en dependencies añade sqflite así:

dependencies:
sqflite: ^2.2.8+2


Después de añadir la biblioteca, en el archivo main.dart debes importarla al inicio del archivo:

import "package:sqflite/sqflite.dart"


Las operaciones en la base de datos se pueden realizar utilizando una clase auxiliar, que contendrá diferentes métodos que se utilizarán para crear una instancia de la base de datos y realizar el CRUD (Create, Read, Update e Delete). Serán funciones asincrónicas y se pueden llamar haciendo clic en los botones en widgets del tipo StatefullWidget, según este ejemplo:

class SQLHelper {
    // Este método cria o banco de dados e uma tabela
    static Future<void> createTables(Database database) async {
        await database.execute("""CREATE TABLE items(id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, title TEXT, description TEXT, createdAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP)""");
    }

    // Este método cria uma instância do banco de dados e cria a tabela se ela já não existir
    static Future<Database> db() async {
        return openDatabase('tasks.db', version: 1, onCreate(Database database, int version) async {
        await createTables(database);});
    }

    static Future<int> createItem(String title, String? description) async {
        final db = await SQLHelper.db();

        final data = {'title': title, 'description': description};
        final id = await db.insert('items', data, conflictAlgorithm: conflictAlgorithm.replace);
        return id;
    }

    static Future<List<Map<String, dynamic>>> getItems() async {
        final db = await SQLHelper.db();
        return db.query('items', orderBy: "id");
    }

    static Future<List<Map<String, dynamic>>> getItem(int id) async {
        final db = await SQLHelper.db();
        return db.query('items', where: "id = ?", whereArgs: [id], limit: 1);
    }

    static Future<int> updateItem(int id, String title, String? description) async {
        final db = await SQLHelper.db();
        final data = {'title': title, 'description': description, 'createdAt': DateTime.now().toString()};
        final result = await db.update('items', data, where: "id = ?", whereArgs: [id]);
        return result;
    }

    static Future<void> deleteItem(int id) async {
        final db = await SQLHelper.db();
        try {
            await db.delete("items", where: "id = ?", whereArgs: [id]);
        } catch (err) {
            debugPrint("Something went wrong when deleting an item: $err");
        }
    }
}


Ten en cuenta que todas las funciones que realizan cualquier operación en la base de datos primero deben crear la instancia de la base de datos mediante el método SQLHelper.db() y los datos deben estar en formato Mapa.

Generando la aplicación y publicarla en Play Store

Una vez que el código de la aplicación esté listo, el siguiente paso es ponerlo a disposición de otros usuarios. Para hacer esto, necesitas tener una cuenta de desarrollador en Google Play Console. Además, antes de publicar, es necesario saber cómo se presentará la aplicación al usuario final, cómo se verá el ícono en el celular y en Play Store, si tendrá pantalla de presentación o no y el nombre de la aplicación.

Para cambiar el nombre de la aplicación, abre el archivo pubspec.yaml y edita la línea name con el nombre deseado. Para cambiar el ícono de la aplicación, coloca el archivo en formato PNG o JPEG con las dimensiones 512x512 pixels en la carpeta assets. A continuación, en el mismo archivo donde cambiamos el nombre, edita la sección flutter para incluir los assets:

flutter:
    ...
    assets:
    - assets/novo_icon.png


Con todo configurado, simplemente ejecuta el siguiente comando en la terminal:

flutter build appbundle


Este comando generará el APK o paquete de aplicación (AAB) de su aplicación. APK es un archivo único que contiene todo el código y los recursos de su aplicación, mientras que AAB es un paquete más pequeño que la tienda Google Play puede optimizar para diferentes dispositivos. En la página de inicio de Play Console, crea una nueva aplicación ingresando su nombre, descripción e ícono.

En la sección de versión de la aplicación, agrega la versión de tu aplicación y sube el archivo APK o AAB que se generó, luego, en la sección Precio y distribución, selecciona el precio y la región de distribución de tu aplicación, aquí también puedes elegir crearla gratis. Finalmente, cuando esté listo/a para publicar, ve a la sección Inicio de la aplicación y haga clic en Publicar aplicación. El proceso de revisión de Google Play Console puede tardar algunas horas o días antes de que la aplicación se apruebe y esté disponible para su descarga en Google Play Store.


Conclusión y próximos pasos

Con base en lo dicho en este artículo, es posible desarrollar proyectos de aplicaciones más complejos, por ejemplo, cifrar la base de datos, crear un servidor, realizar solicitudes desde la aplicación Flutter al servidor y agregar un sistema de inicio de sesión, buscar bibliotecas para trabajar con otras funciones del celular, como uso de la cámara, GPS, Bluetooth, giroscopio, entre otras.

Flutter es una excelente opción para el desarrollo de aplicaciones móviles, que te permite crear interfaces sorprendentes y funcionales con facilidad. Con la base de datos SQLite, puedes almacenar información importante en la aplicación, como una lista de tareas pendientes, por ejemplo. Y con las herramientas disponibles para publicar en Play Store, es simple y relativamente fácil hacer que tu aplicación esté disponible para millones de usuarios.

Con la combinación de estas tecnologías, desarrollar una aplicación puede ser una tarea apasionante y gratificante para cualquier desarrollador.

💡
Las opiniones y comentarios emitidos en este artículo son propiedad única de su autor y no necesariamente representan el punto de vista de Listopro.

Listopro Community da la bienvenida a todas las razas, etnias, nacionalidades, credos, géneros, orientaciones, puntos de vista e ideologías, siempre y cuando promuevan la diversidad, la equidad, la inclusión y el crecimiento profesional de los profesionales en tecnología.