Objetivos

En este proyecto he querido mostrar cómo cualquier desarrollador Web es capaz, aunque no lo sepa, de desarrollar aplicaciones multiplataforma con el mismo código base.

Pudiendo, además, aprovechar las características del sistema que ejecuta la aplicación, tales como la cámara, sistema de archivos, apps externas, sensores, etc.

Ionic y Capacitor

¿Qué es Capacitor?

Capacitor es una herramienta que nos permite encapsular dentro de un proyecto java, un proyecto únicamente web. Permitiéndonos utilizar el IDE: Android Studio para ejecutar un build y exportar la aplicación como .apk.

Adicionalmente también podemos desarrollar para ios, para lo cual deberemos utilizar el proyecto el Swift y realizar el build en un ordenador MacOS.

Esta herramienta fue creada y es mantenida por Ionic, creadores a su vez del framework Ionic.

¿Qué es Ionic?

Como hemos comentado, Ionic es un framework. Esto significa que ofrece todo lo necesario para crear aplicaciones.

Con esta herramienta nos aseguramos de que, al exportar un proyecto con Capacitor, toda la lógica y elementos HTML serán compatibles cuando se utilice nuestra aplicación en cualquier dispositivo.

Esto no nos impide utilizar frameworks externos como, en este caso, Vue, para desarrollar nuestra aplicación. Únicamente deberemos de tener cuidado con ciertos elementos, en esos casos deberemos de utilizar los que nos proveídos por Ionic.

Vue 3

Vue 3 es la última iteración de Vue. En Vue 3 tenemos la disponibilidad de utilizar la nueva Composition API, en vez de la antigua Options API (todavía ampliamente utilizada).

ejemplo_options_api
Options API clásico

Con esta nueva sintaxis podemos crear componentes muy fácilmente:

ejemplo_composition_api
Composition API con script setup.

Puntos clave

Estructura

Esta será la estructura final del proyecto, iremos desglosando paso por paso cómo reproducir la aplicación:

captura3
Estructura general

Requisitos

  1. Necesitaremos Node.js.
  2. Instalamos Ionic CLI y las utilidades necesarias para utilizarlo de forma global:
    • npm install -g @ionic/cli@latest native-run cordova-res
  3. Creamos un nuevo proyecto utilizando la CLI de Ionic:
    • ionic start photo-gallery tabs –type vue –capacitor
  4. Instalamos las dependencias de capacitor necesarias:
    • npm install @capacitor/camera @capacitor/preferences @capacitor/filesystem @ionic/pwa-elements
  5. Con esto ya temenos un template que podemos ejecutar con el comando: ionic serve

Partiendo del template, iremos programando en JavaScript toda la funcionalidad.

Implementar las pantallas

Layout

Primero, vamos a crear un layout compartido para todas las pantallas, de esta forma podemos añadir tantas pantallas como queramos que todas compartirán el mismo layout.

captura5
Tabs view (layout)

Como podemos ver, en este componente hacemos uso de: <ion-router-outlet></ion-router-outlet>.

Éste cumple la misma función que el elemento <router-view></ router-view > de Vue. Éste nos permite inyectar cualquier tipo de componente hijo definido en el router, en el lugar en el que hayamos definido el elemento:

captura6
Router

Aquí definimos los hijos de /tabs/, serán los tabs que utilizaremos para cada pantalla de la aplicación (Tab1 y Tab2).

Todos ellos compartirán el mismo diseño, ya que son encapsulados por el código que hayamos escrito en TabsLayout.

Implementación de la pantalla de cámara

Para implementar la pantalla de cámara nos centraremos en dos archivos: Tab1View.vue (UI) y usePhotoGallery.js (hook con la lógica).

captura4
Estructura Tab1

Interfaz

captura7
Tab 1 (pantalla de cámara)

Lógica

imports_tab1
Imports Tab1

Como podemos ver, importamos las dependencias necesarias para utilizar la cámara, el sistema de archivos, el hook para comprobar si estamos en web o en un dispositivo móvil (isPlatform) y los modales para informar al usuario del estado de la aplicación (alertController, loadingController).

En este caso vamos a omitir la captura del código, ya que encontraréis el link al repositorio con el código al final de este post, pero os voy a dejar una captura de la estructura del hook:

captura8_usePhotoGallery
Hook Tab 1

Cuando queramos utilizar el hook, deberemos de llamarlo de la siguiente forma:

const {photos, takePhoto, deletePhoto, controlLoadingScreen} = usePhotoGallery();

Como podemos comprobar, la sintaxis es similar a la utilizada con los hooks de React (componentes funcionales) o Vue.

Pantalla explorador de archivos

Interfaz android

catura11_file_explorer_mobile
File explorer Android – carpeta Test
catura11_file_explorer_mobile
File explorer Android – carpeta ROOT

Interfaz web

captura10_file_explorer_web
File explorer Web

Implementación de la interfaz

Para ahorrar espacio en el post, voy a centrarme en el código JavaScript y omitir los estilos aplicados:

file_explorer_template_header

Lo primero que nos encontramos en el template es el elemento ion-header.

Dentro de este elemento tenemos el botón back, el cual estará deshabilitado si estamos en la carpeta root que usará la aplicación dentro del sistema de archivos (en /DOCUMENTS).

Como veremos más adelante, ROOT_FOLDER será “my-photo-collections”.

file_explorer_inputs
File explorer – Inputs

Los siguientes elementos serán dos inputs no visibles, estos inputs los utilizaremos para simular a la interacción con el usuario para subir documentos y carpetas (sólo en web).

El flow sería el siguiente:

input_flow
File explorer – Flow

De esta forma no mostramos inputs type file, sino buttons que representan el click de dichos inputs.

En este punto vamos a mostrar el componente FolderContent:

template_folder_content
Template File explorer – FolderContent

Por último, necesitamos que el usuario pueda añadir carpetas y archivos. Para ello implementamos unos simples botones de Ionic que desplieguen una lista vertical de opciones.

Esto se consigue envolviendo uno o varios botones <ionic-button> con <ion-fab-list>:

file_explorer_folder_content
Template File explorer – buttons

Implementación de la lógica

Antes de nada, vamos a crear nuestro propio hook con utilidades que podamos reutilizar. Para ello haremos una función con y exportaremos las variables y métodos declarados dentro de ella para importarlos desde otros archivos.

estructura_file_explorer_logica
Estructura lógica File explorer

Estos hooks los crearemos en la carpeta /composables.

Empecemos por useUtils.js.

En este hook tendremos utilidades relacionadas con archivos y carpetas:

La función mkdirHelper nos permitirá crear cualquier carpeta pasándole una ruta, si la carpeta ya existe, nos devolverá false y mostrará el error en la consola.

captura_mkdirHelper
Función para crear carpetas – File explorer

La función checkRootFolder comprobará si la carpeta ROOT_FOLDER (definida en el archivo Global.js) existe. Sino existe, se muestra un mensaje al usuario indicándole qué carpeta se va a crear y dónde la puede encontrar:

cpatura_checkRootFolder
Función para comprobar la carpeta root – File explorer

Por último, necesitaremos una función para convertir blobs (archivos del sistema) en string en base64:

Ahora pasaremos al archivo FileExplorer.vue. Este archivo es bastante extenso, con lo cual, omitiré algunas partes para simplificarlo.

Todo el código lo podréis encontrar en mi repositorio de GitHub: https://github.com/host4ideas/proyecto-video-post-FMB

Como se está utilizando Vue 3 con la sintaxis <script setup>. Vamos a crear variables reactivas para poder actualizar la vista de archivos y carpetas cada vez que haya algún cambio en ellos.

captura_reactiveFileExplorer
Utilizando hooks – File explorer

Esta reactividad la encontraremos al final del archivo, donde creamos un watch de Vue para llamar a la función loadDocuments cada vez que los datos cambian.

captura_watchOnMounted
Reactividad – File explorer

Esta sería la función loadDocuments, la cual carga los archivos y carpetas de la ruta actual (la ruta la recogemos por props gracias al router):

captura_loadDocuments
Función para cargar elementos – FIle explorer

Como hemos implementado un botón de ir hacia atrás, dicho botón debe de ir adherido a funcionalidad customizada, en este caso no nos vale con ir hacia atrás en el historial.

¿Pero por qué?

La respuesta es la siguiente:

Imaginemos que te encuentras en la carpeta test1:

ROOT_FOLDER/test/test1

Si pulsas el botón hacia atrás y vuelves hasta ROOT_FOLDER, deberemos de comprobar que no hay más carpetas padres, de lo contrario, el usuario podría ir hacia atrás en el historial hasta salir de nuestra app (esto sería perjudicial sobre todo en dispositivos móviles).

Para ello:

captura_handleBackButton
Función para manejar el botón de atrás – File explorer

Vamos a dejar el post en este punto, ya que el objetivo de este post es explicar cómo podéis replicar la funcionalidad de esta aplicación vosotros mismos.

¿Cómo puedo adaptar esto en mi proyecto?

Para adaptar la funcionalidad de esta aplicación para vuestros propios fines, simplemente entrad en el repositorio de GitHub y copiad la funcionalidad. Me he esmerado en que sea modular y no funcione únicamente en este proyecto (la única dependencia es que sea un proyecto Vue y tener la carpeta actual del usuario en una variable currentFolder).

Aquí vamos a terminar por hoy, si os han quedado más dudas podéis contactar conmigo a través de:

LinkedIn: https://www.linkedin.com/in/felix-martinez-bendicho/

Datos de interés

Leave a Comment

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.