Dans les tests unitaires en dart, la solution de base pour vérifier qu'une variable a bien la valeur attendue est l'utilisation de la fonction expect
de flutter_test
qui prend une variable et un matcher. Le problème principal de cette fonction est le typage dynamique de ses arguments. Si le type de la variable ne correspond pas à celui du matcher, une erreur remonte seulement lors de l'exécution du test sans aucune indication au développeur pendant l'écriture de test.
Le package checks
expose plusieurs méthodes pour vérifier la valeur et le type de variable dans la fonction équivalente et propose un support pour l'égalité profonde des collections. Les méthodes disponibles sont automatiquement filtrées selon le type de variable dans la fonction.
Par ailleurs, checks améliore l'expérience de développement en proposant, dans les IDEs modernes, une auto-complétion filtrée directement sur les méthodes proposées. Toutes les méthodes de test peuvent être appelées à la chaîne, une approche déclarative qui favorise la clarté et la lisibilité du code. La librairie propose aussi une possibilité de s'attendre à des valeurs de Futures
ou de Streams
. Enfin, la possibilité de personnaliser les tests efface les limitations en permettant de combiner des tests existants ou de créer des logiques entièrement propres à son contexte.
Bien que peu connue, la librairie checks
offre des avantages significatifs qui rendent l'expérience de test plus explicite et lisible. C'est pourquoi nous en faisons notre solution par défaut dans les projets Flutter et recommandons vivement son adoption.
En Flutter, la gestion du state global se fait via les InheritedWidget
et ChangeNotifier
, mais ces APIs présentent plusieurs défauts, comme la verbosité, la complexité, la difficulté à tester ou l'impossibilité de créer plusieurs states d’un même type.
Riverpod est une librairie de caching réactif, qui permet de résoudre ces problèmes. Inspirée de react-query
, elle propose de servir la donnée via des providers, déclarés en dehors du cycle de vie des widgets, et pouvant automatiquement rebuild les widgets qui les écoutent. Les providers peuvent servir de la donnée asynchrone venant par exemple d’un call API ou une base de donnée locale, gèrent les erreurs et le cache et permettent de facilement définir d’autres fonctionnalités telles que le debounce ou le pull-to-refresh.
Riverpod est aussi compile-safe, propose une API déclarative, est activement maintenue, et est soutenue par une vaste communauté (6k stars sur github, 98% de popularité sur pub.dev). Depuis l’année dernière, nous avons pu expérimenter ses outils de génération de code, qui permettent aux hot reloads de mettre à jour un provider et de réduire encore la verbosité et la complexité de l’API.
Nous recommandons d’utiliser Riverpod qui nous accompagne depuis maintenant 3 ans sur nos projets Flutter de toutes envergures. L’annonce de Riverpod 3, qui devrait permettre de définir et réutiliser des providers ayant en paramètres des types génériques, ne fait que renforcer notre enthousiasme.
En Flutter, les slivers sont un type de widget qui s'intègre dans les vues scrollables et réagissent au scroll pour permettre de confectionner des écrans scrollables animés et complexes. Si les slivers ne présentent pas de difficulté particulière dans leur utilisation, ils sont cependant des widgets bas niveau et leur écriture est complexe.
Si le framework propose un certain nombre de widgets slivers de très haut niveau tels que le SliverAppBar
ou la SliverList
il peut être difficile de personnaliser ses vues scrollables en dehors des classiques proposés. La librairie sliver_tools
propose une collection de widgets slivers prêt à l'emploi qui enrichissent ceux présents nativement dans le framework en apportant un niveau de flexibilité intermédiaire entre les widgets haut niveau et les RenderObjects
bas niveau.
Parmi les plus utilisés, on retrouve MultiSliver
qui permet de combiner plusieurs slivers en un seul afin d'améliorer la qualité de code en découpant les responsabilités ainsi que le SliverPinnedHeader
qui permet de créer des éléments scrollable qui s'accrochent en haut de la vue scrollable pour les maintenir en visibilité et donner un effet de navigation plaisant.
Notre utilisation de sliver_tools
sur les projets est très concluante et n'a pas remonté de limitations. Cette boite à outil fait désormais partie de notre stack standard chez Theodo pour nous permettre de donner vie aux vues scrollables originales imaginées en collaboration avec nos designers.
La cartographie dans les applications mobiles est un sujet complexe en raison des performances et des limitations imposées par certaines librairies pour afficher divers éléments. Traditionnellement, les développeurs se sont tournés vers des solutions comme Mapbox et Google Maps, qui utilisent un moteur graphique en C++ pour rendre les cartes. Ces solutions sont robustes, mais elles ne s’intègrent pas toujours de manière fluide avec Flutter, en particulier pour personnaliser les éléments à afficher sur la carte.
La librairie flutter_map
a été entièrement écrite en Dart avec une API déclarative et composable pour les éléments UI, associée à une approche impérative pour le contrôle manuel, tel que les animations. Cette approche permet aux développeurs de profiter pleinement des avantages de Flutter, notamment en intégrant facilement des widgets sur la carte. Elle bénéficie également d'un écosystème varié d'extensions open source. Nous avons utilisé flutter_map
pour créer des cartes raster très poussées en terme de personnalisation, et avons été impressionnés par sa facilité d’utilisation et ses performances pour les cartes raster.
flutter_map
présente cependant des limitations importantes. L'extension pour les cartes vectorielles souffre de problèmes de performances majeurs, le rendant impraticable pour ce type de cartes. Les transitions entre les niveaux de zoom ne sont pas aussi fluides que celles des SDK comme Mapbox ou Google Maps. Cette limitation nous a poussés à privilégier des solutions alternatives pour les projets nécessitant des cartes vectorielles. Pour résoudre ces problèmes, la communauté travaille activement sur ces problèmes avec une solution utilisant les dernières avancées de Flutter comme Impeller ou flutter_gpu
.
Nous vous recommandons d’essayer flutter_map
pour intégrer des cartes raster à vos projets Flutter, en raison de sa simplicité d’utilisation et de ses performances. Nous vous invitons cependant à rester ouverts aux autres solutions disponibles, notamment dans les cas d'utilisation de cartes vectorielles.
Il existe plusieurs solutions de bases de données locales en Flutter, notamment SharedPreferences, Isar, Hive, ObjectBox, SQLite, etc. Chacune de ces solutions a ses propres avantages et inconvénients en termes de performance, de facilité d'utilisation et de fonctionnalités. Dans ce contexte, nous avons intégré MMKV pour Flutter sur l'un de nos projets. MMKV est une bibliothèque de stockage clé/valeur performante et facile à utiliser, développée par Tencent. Utilisée dans l'application WeChat, MMKV est conçue pour offrir des performances optimales grâce à l'utilisation de mmap
et de protobuf
, permettant de synchroniser la mémoire avec les fichiers et d'encoder/décoder les valeurs efficacement.
L'utilisation de Dart FFI pour les opérations de lecture et d'écriture synchrones permet de tirer pleinement profit de ces performances en Flutter. Nous avons mesuré que l'ouverture d'une base de données MMKV est beaucoup plus économe en ressources CPU que d'autres solutions, comme par exemple Isar, qui peut bloquer un thread pendant plusieurs centaines de millisecondes. L'API de MMKV est claire et minimaliste, ce qui simplifie son intégration et son utilisation dans les projets Flutter. Depuis la migration vers une architecture de plugin fédérée, l'expérience de développement avec MMKV s'est nettement améliorée. De plus, MMKV supporte le chiffrement des données, offrant ainsi une couche supplémentaire de sécurité.
Cependant, MMKV pour Flutter présente quelques limitations. Actuellement, seules les plateformes iOS et Android sont supportées, bien que MMKV en lui-même soit disponible sur iOS, Android, Linux, macOS et Windows. La prise en charge future du web pourrait s'avérer complexe à mettre en œuvre. De plus, une mise à jour mineure récente a supprimé le support pour les architectures Android ARM7 et x86, impactant environ 2% de nos utilisateurs en production.
Malgré ces défis, nous trouvons que MMKV est une solution très intéressante. Elle est simple et extrêmement performante, développée par Tencent, ce qui lui confère une grande fiabilité, et une maintenance à long terme. Bien que très populaire dans les communautés React Native et native, MMKV reste encore méconnue dans l'écosystème Flutter, probablement en raison de la présence de bases de données locales synchrones déjà établies.
Nous recommandons de tester MMKV dans vos projets Flutter. Cependant, il est essentiel de réaliser une évaluation approfondie des plateformes et des appareils utilisés par vos utilisateurs afin d'assurer une intégration réussie et une compatibilité avec les exigences spécifiques de votre projet.
La gestion des formulaires et la validation des entrées utilisateur sont des aspects cruciaux mais complexes du développement d’applications. Reactive Forms, une bibliothèque pour Flutter, propose une gestion des formulaires basée sur le modèle, inspirée par Angular. Chez Theodo, nous l’utilisons pour les projets nécessitant des formulaires, comme ceux de connexion, d’inscription ou de paiement.
Reactive Forms se distingue par son riche écosystème de validateurs prédéfinis, asynchrones et personnalisables. Cette flexibilité facilite la gestion des règles de validation complexes et spécifiques à chaque projet. Cependant, la prise en main peut être difficile et le code requis pour définir un formulaire est parfois verbeux. Le système de typage pourrait également être amélioré. Une extension utilisant la génération de code est en cours de développement pour simplifier cette fonctionnalité.
Reactive Forms s’intègre bien avec les outils de gestion d’état comme Riverpod ou BLoC, permettant de réagir efficacement aux changements d’état du formulaire. Il permet aussi de tester unitairement chaque règle de validation, garantissant qu’aucune régression n’est introduite lors des mises à jour. Cela améliore la productivité et la maintenabilité des applications.
La documentation et le support communautaire de Reactive Forms sont excellents, avec une communauté active et des mises à jour régulières. Le package compte 458 étoiles sur GitHub et 839 likes sur Pub, et l’écosystème de validateurs créés par la communauté est un atout majeur.
Nous recommandons d’essayer Reactive Forms pour vos projets Flutter en raison de ses capacités de validation robustes, de sa flexibilité et de son intégration fluide avec les outils de gestion d’état. Toutefois, étant donné la complexité initiale et la verbosité du code, il convient de le faire avec précaution.
Les délais de mise à jour constituent un défi majeur en développement mobile, car chaque version doit être validée par les stores, retardant ainsi la disponibilité des correctifs urgents pour les utilisateurs. De plus, il faut attendre que chaque utilisateur mette à jour l'application. Shorebird, annoncé début 2023 par Éric Seidel, créateur de Flutter, est une solution open-source pour déployer des mises à jour d'applications Flutter over-the-air (OTA) sans passer par les stores. Cette approche permet de déployer des mises à jour mineures directement via leurs serveurs, accélérant ainsi le cycle de développement et de déploiement.
L'intégration de Shorebird dans un projet Flutter est simple mais présente certains inconvénients. Le coût peut être un frein pour certaines équipes, et la commande shorebird patch est plus lente que flutter build, ce qui peut ralentir le processus de mise à jour, surtout dans un environnement de QA actif plusieurs fois par jour. Actuellement, Shorebird supporte uniquement iOS et Android, excluant les applications desktop. De plus, l'application doit être redémarrée pour exécuter le code mis à jour, ce qui peut affecter l'expérience utilisateur.
Malgré ces inconvénients, Shorebird offre une fonctionnalité précieuse en étant la seule solution de mise à jour OTA pour Flutter. Cet outil répond à un besoin crucial en développement mobile, et respecte les règles des stores en ne mettant à jour que du code interprété.
Bien que la CLI de Shorebird ait récemment atteint la version 1, elle présente encore quelques instabilités, mais ces problèmes sont rapidement résolus, l'équipe de développement étant attentive aux retours de la communauté.
Shorebird montre des résultats prometteurs et a le potentiel de transformer la gestion des mises à jour dans les applications Flutter. Nous recommandons de tester cette technologie pour évaluer son impact sur vos cycles de développement et de déploiement.
Lors de nos développements, nous nous appuyons sur des outils de génération de code tels que freezed
, json_serializable
et build_runner
. Ces outils, bien qu'essentiels pour adresser des problématiques récurrentes telles que la désérialisation, l'immutabilité ou l’égalité profonde, introduisent des inefficacités et nuisent à l'expérience développeur en imposant des générations de code manuelles fréquentes. Ils tendent également à encombrer nos projets avec des fichiers générés.
Les macros Dart, actuellement en beta, promettent de transformer cette dynamique en intégrant la métaprogrammation directement dans le compilateur. Cette méthode permet aux développeurs de générer du code à mesure qu'ils écrivent. Elles améliorent ainsi la productivité en réduisant les générations répétitives et en maintenant une base de code plus propre. Les macros pourraient cependant compromettre la simplicité et la lisibilité de Dart, qui sont essentielles pour sa facilité d'apprentissage. Dart est réputé pour être un langage "ennuyeux" dans le bon sens du terme : prévisible et stable. L'introduction des macros, par contraste, introduit une dimension de complexité qui n'est pas traditionnellement associée à Dart.
Si les macros semblent menacer cette transparence, le système d'augmentation proposé permet aux développeurs de visualiser le code augmenté d'un simple clic dans leur environnement de développement, maintenant ainsi une compréhension claire du code exécuté.
Notons que les macros seront surtout utilisées par les mainteneurs de librairies, comme freezed
ou json_serializable
, et non au quotidien par tous les développeurs Flutter. Nous serons attentifs à la manière dont les mainteneurs et la communauté accueilleront et mettront en œuvre cette nouvelle fonctionnalité.
Nous sommes optimistes sur le potentiel des macros Dart pour simplifier et améliorer la génération de code dans nos applications Flutter. Bien que les macros soient actuellement en phase expérimentale, l'équipe Dart prévoit sur sa roadmap une version stable pour début 2025.
Chez Theodo, nous pensons que les tests automatiques sont une des meilleures manières de prévenir les régressions fonctionnelles ou visuelles sur les applications mobiles. Cependant, les solutions de test existantes en Flutter, telles que les tests unitaires et les tests de widgets, ne couvrent pas toujours de manière exhaustive les scénarios de bout en bout (E2E).
Patrol est une librairie Flutter qui simplifie l'écriture et l'exécution des tests E2E. Elle intègre une API qui permet d'interagir très simplement avec les fonctionnalités natives de l'appareil de test telles que les permissions, les notifications ou les paramètres. La librairie patrol_finders qui est désormais indépendante de Patrol, offre en outre du sucre syntaxique rendant l'écriture des tests plus intuitive et moins sujette aux erreurs.
Chez Theodo, nous abordons les tests automatiques en utilisant plutôt des tests unitaires, ainsi que des tests d'UI adaptatif (également connus sous le nom de golden tests) grâce à des outils comme adaptive_test. Ils garantissent une couverture fonctionnelle complète tout en vérifiant visuellement l'interface utilisateur.
Pour les tests E2E, Patrol offre une API extensive, mais ne fonctionnera qu'avec des applications Flutter. Pour ce besoin-là notre choix actuel se porte sur Maestro, une technologie qui a fait ses preuves et dont nous pouvons mutualiser les apprentissages avec les équipes React Native et natives iOS/Android. Nous trouvons cependant que Patrol résout le problème des tests E2E en Flutter de manière intéressante et intuitive.
Pour les développeurs Flutter qui veulent améliorer leurs tests E2E, Patrol mérite d'être exploré, en particulier si vous voulez utiliser un outil qui s'intégrera parfaitement à vos tests Flutter existants. Même si notre choix actuel se porte sur Maestro, nous continuerons de suivre l’évolution de Patrol.
La gestion des états globaux d’une application est cruciale pour sa maintenabilité et ses performances. Diverses solutions ont été proposées pour Flutter, et une nouvelle option mérite notre attention : Signals.
Signals est une bibliothèque innovante pour Flutter, facilitant la gestion des états avec des signaux réactifs. Inspirée par les concepts de réactivité de l'écosystème JavaScript, tel que preact, elle propose une réactivité fine où chaque signal représente une valeur encapsulée dans une coque réactive. Les signaux peuvent être des états simples ou des calculs dérivés d'autres états, formant un graphe acyclique de dépendances.
Signals offre plusieurs avantages clés :
L’API des Signals est similaire à celle de Riverpod, mais avec un système push-pull plus avancé. Contrairement à Riverpod, qui gère également l’injection de dépendances réactives, Signals se concentre exclusivement sur la gestion des états. Pour faire scaler une application Flutter, il faut donc utiliser une solution d’injection de dépendances comme InheritedWidget, Provider ou GetIt.
Chez Theodo, nous explorons les avantages potentiels de Signals, une technologie prometteuse qui pourrait unifier les pratiques de gestion d’état dans la communauté Flutter, actuellement fragmentée. Bien que Signals soit encore relativement nouvelle et peu utilisée dans des projets de production à grande échelle, sa popularité croissante dans d’autres frameworks web indique une convergence vers une technique standard de programmation réactive.
Nous vous encourageons à expérimenter avec Signals, tout en gardant à l’esprit qu’elle n’a pas encore atteint le niveau d’adoption de solutions établies comme Riverpod ou BLoC. Gardez un œil sur ce package pour ses évolutions futures et son potentiel d'intégration.
La gestion locale des données est particulièrement importante pour les applications qui doivent fonctionner hors ligne ou qui nécessitent un haut niveau de confidentialité et de sécurité. Une base de données efficace permet de gérer les données de manière optimale, d'assurer une rapidité d'accès suffisante et de garantir une expérience utilisateur fluide.
Isar est une librairie de base de données NoSQL rapide et facile à utiliser, spécialement développée pour les applications Flutter. Conçue pour remplacer Hive, une base de données clé/valeur largement utilisée dans l'écosystème Flutter, Isar promet des performances élevées grâce à son moteur écrit en Rust. Elle offre des fonctionnalités avancées telles que les index composites, les opérations asynchrones et le support multiplateforme (iOS, Android, Desktop). Cependant, nous avons relevé plusieurs points qui justifient une approche prudente.
La dernière période d'activité significative sur le dépôt GitHub d'Isar remonte à un an, ce qui soulève des préoccupations quant à son développement et à sa maintenance. De plus, des tests internes réalisés dans le cadre de notre initiative Kaizen ont montré que le temps d'ouverture des données chiffrées avec Isar pouvait ralentir l'ouverture d'une application de plusieurs centaines de millisecondes. Cette limitation qui survient à un moment critique de l'expérience utilisateur, est absente d'autres solutions plus matures comme MMKV. En dépit des promesses de performances élevées garanties par son moteur Rust, Isar présente donc des limitations en termes de rapidité et de fiabilité qui sont actuellement bloquantes pour une application.
Nous recommandons de choisir d'autres solutions qu'Isar pour gérer le stockage de données locales, comme MMKV ou ObjectBox par exemple. Bien que la librairie Isar soit prometteuse, il est préférable d'attendre qu'elle atteigne un niveau de maturité et de stabilité plus élevé avant de l'adopter pour des projets de production.
Retrouvez l'avis de nos experts sur les techniques, plateformes, outils, langages et frameworks associés aux principales technologies mobiles que nous utilisons au quotidien chez BAM : React Native, Flutter et Native.