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

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

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_env
ou dart run tools/gen_env.dart
. Il s'agit d'un outil que j'ai créé moi-même, afin de créer le env
dossier 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 .env
fichier puis les mettra dans le dart-define
drapeau lorsque l'application Flutter est courir ou construire.

2.3. Génération de fichiers
Une fois que nous avons le env
dossier, 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 l10n
aide à générer la classe S
à partir des .arb
fichiers dans les resources
modules.

Enfin, la commande melos run force_build_all
permet 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_dev
l'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.

Les deux dossiers app_icon
et splash
contiennent les fichiers app-icon.yaml
et splash.yaml
nous 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 app
dossier 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 AppBloc
pour 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 base
dossier est l'endroit où nous allons coder les classes de base telles que BasePageState
, BaseBloc
et .BaseEvent
BaseState
Le dossier shared_view
est 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_image
sont des vues de groupe courantes affichées dans les écrans de connexion et d'enregistrement.
Le dossier common_view
est 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_scaffold
peuvent ê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 config
est l'endroit où j'appellerai les fonctions de configuration pour le module d'application telles queFirebase.initializeApp()
Le dossier di
est destiné à la configuration DI pour le module d'application.
Le dossier exception_handler
prendra en charge toutes les exceptions du projet.
Le dossier helper
contient les classes Helper utilisées exclusivement pour le module app.
Le dossier navigation
est 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 resource
est 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 ui
est l'endroit où je vais coder l'interface utilisateur et le bloc pour chaque écran.
Le dossier utils
est l'endroit où je vais coder les fonctions utils utilisées exclusivement pour le module app.
3.2. Le module de domaine

Semblables au module d'application, les dossiers config
et di
servent à des fins similaires dans le module de domaine.
Le dossier entity
contient des entités de classe telles que User et Booking.
Le dossier navigation
contient la AppNavigator
classe responsable de push
, replace
et pop
des écrans.
Le dossier repository
est l'endroit où nous déclarerons les classes Abstract Repository dans le projet.
Le dossier usecase
est l'endroit où nous déclarerons les cas d'utilisation dans le projet.
3.3. Le module de données

De même, les dossiers config
et di
ont le même but ici.
Le dossier graphql
contient .graphql
et schema.graphql
si votre projet utilise GraphQL. Le fichier build.yaml
est utilisé pour configurer les fichiers générés liés à GraphQL.
Le dossier repository
contient les classes d'implémentation du référentiel. Il contient également plusieurs dossiers :
converter
: pour coder les classes Convertermapper
: pour coder les classes Mapper qui sont ensuite utilisées pour mapper les modèles de données dans les entitésmodel
: pour coder les classes de modèle de donnéessource
: 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

C'est un module simple, il ne contient que des .arb
fichiers qui fournissent des chaînes pour la localisation. Ce module est injecté dans deux autres modules, app
et domain
ainsi les classes UI, Bloc, Entity et Use Case peuvent l'utiliser.
3.5. Le 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

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 .github
sert à configurer CI/CD si votre projet utilise Github Actions.
Le dossier .lefthook
et le fichier lefthook.yml
permettent de configurer la convention Git à l'aide de la bibliothèque lefthook.
Le dossier .vscode
sert à déclarer les paramètres VSCode utilisés pour ce projet.
Le dossier .env
sert à déclarer des secrets pour chaque environnement.
Le dossier tools
contient des outils que j'ai créés.
Le fichier analysis_options.yaml
sert à déclarer les règles de linter de deux bibliothèques flutter_lints et dart_code_metrics .
Le fichier bitbucket-pipelines.yml
sert à configurer CI si votre projet utilise Bitbucket Pipelines.
Le fichier makefile
garde une trace de toutes les commandes utilisées dans le projet. Par exemple, la commande make format
aide à formater tout le code avec .dart
l'extension de fichier dans le projet. Il existe de nombreuses autres commandes telles que celle make test
qui exécute le test unitaire sur tous les modules.
Le fichier melos.yaml
sert à 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.