Flutter — Creando un Proyecto Repetitivo perfecto desde cero

Dec 01 2022
Crédito: Nguyễn Thành Minh (desarrollador de Android) Todos hemos estado allí, lo que quiero decir es que todos hemos intentado construir un proyecto repetitivo, ya sea para el trabajo o como un proyecto personal. Un buen proyecto repetitivo nos ahorrará mucho tiempo al resolver problemas comunes como llamar a API, procesar datos de bases de datos y, quizás lo más importante, hacer que nuestro código sea organizado y coherente para que las personas puedan tener más facilidad para entender nuestro código.

Crédito: Nguyễn Thành Minh (desarrollador de Android)

Todos hemos estado allí, lo que quiero decir es que todos hemos tratado de construir un proyecto repetitivo, ya sea para el trabajo o como un proyecto personal. Un buen proyecto repetitivo nos ahorrará mucho tiempo al resolver problemas comunes como llamar a API, procesar datos de bases de datos y, quizás lo más importante, hacer que nuestro código sea organizado y coherente para que las personas puedan tener más facilidad para entender nuestro código. Esta serie pretende compartir un proyecto repetitivo que he construido, usado y mantenido durante los últimos dos años. No solo eso, compartiré mi enfoque para codificarlo, así como soluciones a problemas comunes que puedan surgir. Dicho esto, esta es la parte 1: Explorando el Proyecto.

Ver en Github

Proyecto Arquitectura

1. Características

Características construidas en este proyecto:

  • Arquitectura: Arquitectura Limpia
  • Gestión de estado: flutter_bloc
  • Navegación: auto_ruta
  • DI: get_it , inyectable
  • API REST: dio
  • GraphQL: artemisa , graphql_flutter
  • Base de datos: caja de objetos
  • Preferencias compartidas: crypted_shared_preferences
  • Clase de datos: congelado
  • Pelusa: dart_code_metrics , flutter_lints
  • CI/CD: acciones de Github, canalizaciones de Bitbucket
  • Prueba unitaria: mocktail , block_test
  • Paginación: infinite_scroll_pagination
  • Utilidades : rxdart , dartx , async
  • Generador de activos: flutter_gen_runner , flutter_launcher_icons , flutter_native_splash
  • Otras características: reintento automático de solicitudes de API fallidas, token de actualización,...

Para ejecutar este proyecto según lo previsto, la versión de Flutter debe ser (exactamente) 3.3.9 y la versión 2.8.0 de Melos debe estar instalada. Si desea tener una convención de Git para su equipo, también puede instalar lefthook . Hablaré sobre Melos y Lefthook en las últimas partes de la serie. Por ahora, así es como se ejecuta el proyecto.

Nota: utilicé VSCode y macOS, por lo que no puedo garantizar que también funcione en Android Studio o Windows.

2.1. Instalando Melos

Clean Architecture requiere que dividamos el proyecto en múltiples módulos (paquetes), por lo que necesitamos una herramienta para administrar paquetes: Melos. Para instalar Melos:

dart pub global activate melos 2.8.0

export PATH="$PATH:<path to flutter>/flutter/bin"
export PATH="$PATH:<path to flutter>/flutter/bin/cache/dart-sdk/bin"
export PATH="$PATH:~/.pub-cache/bin"

Después de instalar Melos, deberá ejecutar el comando make gen_envo dart run tools/gen_env.dart. Esta es una herramienta que creé yo mismo, para crear la envcarpeta para que podamos declarar secretos como Google API_KEY, credenciales de autenticación básicas, etc. Esta herramienta leerá los valores en el .envarchivo y luego los colocará en la dart-definebandera cuando la aplicación Flutter esté correr o construir.

Carpeta Env y archivos .env correspondientes a sus entornos

2.3. Generación de archivos

Una vez que tenemos la envcarpeta, ahora solo necesitamos ejecutar el comando make sync. Esta es una abreviatura de tres comandos:

melos bootstrap
melos run l10n
melos run force_build_all

A continuación, el comando melos run l10nayuda a generar la clase Sa partir de los .arbarchivos de los resourcesmódulos.

Finalmente, el comando melos run force_build_allayuda a generar los archivos .g.dart, .freezed.dartetc. en todos los paquetes que tiene la biblioteca build_runner.

¡ Ahora, corre make run_devpara ejecutar la aplicación y disfruta!

3. Proyecto de Arquitectura

En total, tenemos 6 módulos (paquetes)

  • aplicación
  • Dominio
  • Datos
  • Recursos
  • Compartido
  • Inicializador

3.1. El módulo de la aplicación

Aquí es donde codificaremos nuestra UI y Bloc. Además, es donde almacenaremos nuestros recursos como colores, dimensiones y estilos de texto. Aquí es también donde declararemos nuestra función main() como punto de entrada para que se ejecute nuestra aplicación.

Módulo de aplicación

Ambas carpetas app_icony splashcontienen los archivos app-icon.yaml, splash.yamlpor lo que podemos configurar el ícono de la aplicación y la pantalla de inicio según las instrucciones de las bibliotecas flutter_launcher_icons y flutter_native_splash .

La appcarpeta contiene AppBloc, que es un bloque provisto en MaterialApp, un lugar desde donde todas las pantallas de la aplicación pueden recuperar datos, en otras palabras; es como un Bloc compartido a todas las pantallas. Puede usarlo AppBlocpara tareas que necesitan actualizar la interfaz de usuario de todas las pantallas, como la configuración de idioma, el cambio de modo oscuro/modo claro.

La carpeta es donde codificaremos clases base basecomo BasePageState, BaseBlocy .BaseEventBaseState

La carpeta shared_viewes donde codificaré la interfaz de usuario compartida entre muchas pantallas, y luego se puede usar para otros proyectos. Por ejemplo: app_text_field, circle_imageson vistas de grupo comunes que se ven en las pantallas de inicio de sesión y registro.

La carpeta common_viewes donde codificaré la interfaz de usuario compartida entre muchas pantallas, y luego se puede usar para otros proyectos. Por ejemplo, common_dialog, common_app_bar, common_scaffoldse puede reutilizar en muchos proyectos, aunque es posible que queramos ajustarlos un poco para que coincidan con el estilo.

La carpeta configes donde llamaré a las funciones de configuración para el módulo de la aplicación, comoFirebase.initializeApp()

La carpeta dies para la configuración de DI para el módulo de la aplicación.

La carpeta exception_handlerse encargará de todas las excepciones en el proyecto.

La carpeta helpercontiene clases auxiliares utilizadas exclusivamente para el módulo de la aplicación.

La carpeta navigationes donde declararemos las pantallas, así como los cuadros de diálogo y las hojas inferiores utilizadas en la aplicación.

La carpeta resourcees donde declararemos recursos para la interfaz de usuario, como colores, dimensiones y estilos de texto.

La carpeta uies donde codificaré la interfaz de usuario y el bloque para cada pantalla.

La carpeta utilses donde codificaré las funciones de utilidades utilizadas exclusivamente para el módulo de la aplicación.

3.2. El módulo de dominio

módulo de dominio

Al igual que el módulo de la aplicación, las carpetas configy ditienen propósitos similares en el módulo de dominio.

La carpeta entitycontiene entidades de clase como Usuario y Reserva.

La carpeta navigationtiene la AppNavigatorclase, que es responsable de push, replacey popde las pantallas.

La carpeta repositoryes donde declararemos las clases del repositorio abstracto en el proyecto.

La carpeta usecasees donde declararemos los casos de uso en el proyecto.

3.3. El módulo de datos

Módulo de datos

Del mismo modo, las carpetas configy ditienen el mismo propósito aquí.

La carpeta graphqlcontiene .graphqly schema.graphqlsi su proyecto usa GraphQL. El archivo build.yamlse usa para configurar los archivos generados que están relacionados con GraphQL.

La carpeta repositorycontiene clases de implementación de repositorio. También contiene varias carpetas:

  • converter: para codificar clases de convertidor
  • mapper: para codificar clases de mapeador que luego se utilizan para mapear modelos de datos en entidades
  • model: para codificar clases de modelos de datos
  • source: para codificar clases base como RestApiClient, GraphQlApiClient y proporcionar fuentes de datos en el proyecto como AppApiService, AppDatabase, AppSharedPreferences, LocationDataSource
Módulo de Recursos

Este es un módulo simple, solo contiene .arbarchivos que proporcionan cadenas para la localización. Este módulo se inyecta en otros dos módulos, apppor domainlo que las clases UI, Bloc, Entity y Use Case pueden usarlo.

3.5. El módulo compartido

Módulo compartido

Como sugiere el nombre, este módulo proporciona constantes, clases de excepción personalizadas, clases de ayuda, mixins y funciones utils para que las usen otros módulos.

3.6. El módulo inicializador

Módulo inicializador

Este módulo solo contiene la clase AppInitializer. Esta clase es responsable de reunir todas las configuraciones de otros módulos en una init()función; La main()función solo tiene que llamar a esta init()función para recuperar todas las configuraciones de todos los módulos.

3.7. Otras carpetas y archivos

La carpeta .githubes para configurar CI/CD si su proyecto usa Github Actions.

La carpeta .lefthooky el archivo lefthook.ymlson para configurar la convención de Git usando la biblioteca de gancho izquierdo.

La carpeta .vscodees para declarar la configuración de VSCode utilizada para este proyecto.

La carpeta .enves para declarar secretos para cada entorno.

La carpeta toolscontiene herramientas que he creado.

El archivo analysis_options.yamles para declarar las reglas de linter de dos bibliotecas, flutter_lints y dart_code_metrics .

El archivo bitbucket-pipelines.ymles para configurar CI si su proyecto usa Bitbucket Pipelines.

El archivo makefilerealiza un seguimiento de todos los comandos utilizados en el proyecto. Por ejemplo, el comando make formatayuda a formatear todo el código con .dartextensión de archivo dentro del proyecto. Hay muchos más comandos como make testel que ejecuta Unit Test en todos los módulos.

El archivo melos.yamles para configurar Melos.

Conclusión

¿Por qué necesitamos crear otro módulo solo para la configuración de inicio? ¿Qué son las clases de mapeador, entidad y caso de uso? Los repasaré en la segunda parte de esta serie: la arquitectura limpia.