Flutter - Construire un projet de Boilerplate parfait à partir de zéro

Dec 01 2022
Crédit : Nguyễn Thành Minh (développeur Android) Nous sommes tous passés par là - ce que je veux dire, c'est que nous avons tous essayé de créer un projet passe-partout, que ce soit pour le travail ou en tant que projet personnel. Un bon projet passe-partout nous fera gagner beaucoup de temps en résolvant des problèmes courants tels que l'appel d'API, le traitement de données à partir de bases de données et peut-être le plus important - rendre notre code organisé et cohérent afin que les gens puissent avoir plus de facilité à essayer de comprendre notre code.

Crédit : Nguyễn Thành Minh (développeur Android)

Nous sommes tous passés par là - ce que je veux dire, c'est que nous avons tous essayé de construire un projet passe-partout, que ce soit pour le travail ou en tant que projet personnel. Un bon projet passe-partout nous fera gagner beaucoup de temps en résolvant des problèmes courants tels que l'appel d'API, le traitement de données à partir de bases de données et peut-être le plus important - rendre notre code organisé et cohérent afin que les gens puissent avoir plus de facilité à essayer de comprendre notre code. Cette série est destinée à partager un projet passe-partout que j'ai construit, utilisé et entretenu au cours des deux dernières années. De plus, je partagerai mon approche du codage, ainsi que des solutions aux problèmes courants qui pourraient survenir. Cela dit, c'est la partie 1 : Exploration du projet.

Voir sur Github

Architecture du projet

1. Caractéristiques

Fonctionnalités intégrées à ce projet :

  • Architecture : architecture épurée
  • Gestion des états : flutter_bloc
  • Navigation : auto_route
  • DI : get_it , injectable
  • API REST : dio
  • GraphQL : artémis , graphql_flutter
  • Base de données : objectbox
  • Préférences partagées : encrypted_shared_preferences
  • Classe de données : gelé
  • Charpie : dart_code_metrics , flutter_lints
  • CI/CD : actions Github, pipelines Bitbucket
  • Test unitaire : mocktail , bloc_test
  • Pagination : infini_scroll_pagination
  • Utilitaires : rxdart , dartx , async
  • Générateur d'actifs : flutter_gen_runner , flutter_launcher_icons , flutter_native_splash
  • Autres fonctionnalités : relance automatique des demandes d'API ayant échoué, jeton d'actualisation,…

Pour exécuter ce projet comme prévu, votre version de Flutter doit être (exactement) la 3.3.9 et la version 2.8.0 de Melos doit être installée. Si vous souhaitez avoir une convention Git pour votre équipe, vous pouvez également installer lefthook . Je parlerai de Melos et Lefthook dans les dernières parties de la série. Pour l'instant, voici comment exécuter le projet.

Remarque : J'ai utilisé VSCode et macOS, je ne peux donc pas garantir qu'il fonctionnera également sur Android Studio ou Windows.

2.1. Installation de Melos

L'architecture propre nous oblige à diviser le projet en plusieurs modules (packages), c'est pourquoi nous avons besoin d'un outil de gestion des packages - Melos. Pour installer 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"

Après avoir installé Melos, vous devrez alors exécuter la commande make gen_envou dart run tools/gen_env.dart. Il s'agit d'un outil que j'ai créé moi-même, afin de créer le envdossier afin que nous puissions déclarer des secrets tels que Google API_KEY, les informations d'identification de base, etc. Cet outil lira les valeurs dans le .envfichier puis les mettra dans le dart-definedrapeau lorsque l'application Flutter est courir ou construire.

Dossier Env et fichiers .env correspondant à leurs environnements

2.3. Génération de fichiers

Une fois que nous avons le envdossier, nous n'avons plus qu'à exécuter la commande make sync. Il s'agit d'un raccourci pour trois commandes :

melos bootstrap
melos run l10n
melos run force_build_all

Ensuite, la commande melos run l10naide à générer la classe Sà partir des .arbfichiers dans les resourcesmodules.

Enfin, la commande melos run force_build_allpermet de générer les fichiers .g.dart, .freezed.dart, etc. sur tous les packages contenant la bibliothèque build_runner.

Maintenant, lancez make run_devl'application et profitez-en !

3. Architecture du projet

Au total, nous avons 6 modules (forfaits)

  • Application
  • Domaine
  • Données
  • Ressources
  • partagé
  • Initialiseur

3.1. Le module d'application

C'est ici que nous coderons notre interface utilisateur et notre bloc. De plus, c'est là que nous stockerons nos ressources telles que les couleurs, les dimensions et les styles de texte. C'est également là que nous déclarerons notre fonction main() comme point d'entrée pour que notre application s'exécute.

Module d'application

Les deux dossiers app_iconet splashcontiennent les fichiers app-icon.yamlet splash.yamlnous pouvons donc configurer l'icône de l'application et l'écran de démarrage comme indiqué par les bibliothèques flutter_launcher_icons et flutter_native_splash .

Le appdossier contient AppBloc, qui est un bloc fourni dans MaterialApp, un endroit où tous les écrans de l'application peuvent récupérer des données, en d'autres termes ; c'est comme un Bloc partagé sur tous les écrans. Vous pouvez l'utiliser AppBlocpour les tâches nécessitant la mise à jour de l'interface utilisateur de tous les écrans, telles que le réglage de la langue, la commutation entre le mode sombre et le mode clair.

Le basedossier est l'endroit où nous allons coder les classes de base telles que BasePageState, BaseBlocet .BaseEventBaseState

Le dossier shared_viewest l'endroit où je vais coder l'interface utilisateur partagée entre de nombreux écrans, et il peut ensuite être utilisé pour d'autres projets. Par exemple : app_text_field, circle_imagesont des vues de groupe courantes affichées dans les écrans de connexion et d'enregistrement.

Le dossier common_viewest l'endroit où je vais coder l'interface utilisateur partagée entre de nombreux écrans, et il peut ensuite être utilisé pour d'autres projets. Par exemple, common_dialog, common_app_bar, common_scaffoldpeuvent être réutilisés dans de nombreux projets, bien que nous ayons peut-être besoin de les modifier un peu pour qu'ils correspondent au style.

Le dossier configest l'endroit où j'appellerai les fonctions de configuration pour le module d'application telles queFirebase.initializeApp()

Le dossier diest destiné à la configuration DI pour le module d'application.

Le dossier exception_handlerprendra en charge toutes les exceptions du projet.

Le dossier helpercontient les classes Helper utilisées exclusivement pour le module app.

Le dossier navigationest l'endroit où nous déclarerons les écrans ainsi que les boîtes de dialogue et les feuilles inférieures utilisées dans l'application.

Le dossier resourceest l'endroit où nous déclarerons les ressources pour l'interface utilisateur telles que les couleurs, les dimensions et les styles de texte.

Le dossier uiest l'endroit où je vais coder l'interface utilisateur et le bloc pour chaque écran.

Le dossier utilsest l'endroit où je vais coder les fonctions utils utilisées exclusivement pour le module app.

3.2. Le module de domaine

module de domaine

Semblables au module d'application, les dossiers configet diservent à des fins similaires dans le module de domaine.

Le dossier entitycontient des entités de classe telles que User et Booking.

Le dossier navigationcontient la AppNavigatorclasse responsable de push, replaceet popdes écrans.

Le dossier repositoryest l'endroit où nous déclarerons les classes Abstract Repository dans le projet.

Le dossier usecaseest l'endroit où nous déclarerons les cas d'utilisation dans le projet.

3.3. Le module de données

Module de données

De même, les dossiers configet diont le même but ici.

Le dossier graphqlcontient .graphqlet schema.graphqlsi votre projet utilise GraphQL. Le fichier build.yamlest utilisé pour configurer les fichiers générés liés à GraphQL.

Le dossier repositorycontient les classes d'implémentation du référentiel. Il contient également plusieurs dossiers :

  • converter: pour coder les classes Converter
  • mapper: pour coder les classes Mapper qui sont ensuite utilisées pour mapper les modèles de données dans les entités
  • model: pour coder les classes de modèle de données
  • source: pour coder des classes de base telles que RestApiClient, GraphQlApiClient ainsi que fournir des sources de données dans le projet comme AppApiService, AppDatabase, AppSharedPreferences, LocationDataSource
Module Ressources

C'est un module simple, il ne contient que des .arbfichiers qui fournissent des chaînes pour la localisation. Ce module est injecté dans deux autres modules, appet domainainsi les classes UI, Bloc, Entity et Use Case peuvent l'utiliser.

3.5. Le module partagé

Module partagé

Comme son nom l'indique, ce module fournit des constantes, des classes d'exception personnalisées, des classes d'assistance, des mixins et des fonctions utils à utiliser par d'autres modules.

3.6. Le module d'initialisation

Module d'initialisation

Ce module contient uniquement la classe AppInitializer. Cette classe est chargée de rassembler toutes les configurations des autres modules dans une init()fonction ; La main()fonction n'a qu'à appeler cette init()fonction pour récupérer toutes les configurations de tous les modules.

3.7. Autres dossiers et fichiers

Le dossier .githubsert à configurer CI/CD si votre projet utilise Github Actions.

Le dossier .lefthooket le fichier lefthook.ymlpermettent de configurer la convention Git à l'aide de la bibliothèque lefthook.

Le dossier .vscodesert à déclarer les paramètres VSCode utilisés pour ce projet.

Le dossier .envsert à déclarer des secrets pour chaque environnement.

Le dossier toolscontient des outils que j'ai créés.

Le fichier analysis_options.yamlsert à déclarer les règles de linter de deux bibliothèques flutter_lints et dart_code_metrics .

Le fichier bitbucket-pipelines.ymlsert à configurer CI si votre projet utilise Bitbucket Pipelines.

Le fichier makefilegarde une trace de toutes les commandes utilisées dans le projet. Par exemple, la commande make formataide à formater tout le code avec .dartl'extension de fichier dans le projet. Il existe de nombreuses autres commandes telles que celle make testqui exécute le test unitaire sur tous les modules.

Le fichier melos.yamlsert à configurer Melos.

Conclusion

Pourquoi avons-nous besoin de créer un autre module juste pour la configuration init ? Que sont les classes de mappeur, d'entité et de cas d'utilisation ? Je les passerai en revue dans la deuxième partie de cette série - l'architecture propre.