Séparation des préoccupations dans les applications à flutter
Udn Webber
Développeur de flutter
Récemment, je devais implémenter des pas à bord pour Gestionnaire de tâches UDN[dix] Nouveaux arrivants! C'était une tâche très importante car beaucoup de nouveaux utilisateurs étaient sur le point de découvrir la plate-forme avec le Une annonce incroyablement drôle nous avons été créées au Super Bowl[dix]! ✨
via Gestionnaire de tâches UDN[dix]
La procédure pas à pas permet à nos nombreux nouveaux utilisateurs, qui ne savent peut-être pas Gestionnaire de tâches UDN[dix]Pourtant, pour comprendre rapidement comment utiliser plusieurs fonctionnalités de l'application. C'est un effort continu, tout comme le nouveau Gestionnaire de tâches UDN[dix]Université ressources que nous poursuivons! 🚀
Heureusement, l'architecture logicielle derrière le Gestionnaire de tâches UDN[dix]Application mobile Flutter m'a permis d'implémenter cette fonctionnalité assez rapidement, même en réutilisant les vrais widgets de l'application! Cela signifie que la procédure pas à pas est dynamique, réactive et correspond exactement aux écrans d'application réels de l'application et continueront à, même lorsque les widgets vont évoluer.
J'ai également capable de mettre en œuvre la fonctionnalité en raison de la bonne séparation des préoccupations.
Voyons ce que je veux dire ici. 🤔
Séparation des préoccupations
La conception d'une architecture logicielle est l'un des sujets les plus complexes des équipes d'ingénierie. Parmi toutes responsabilités, il est toujours difficile d'anticiper les futures évolutions logicielles. C'est pourquoi créer une architecture bien couché et découplée peut vous aider et vos coéquipiers avec beaucoup de choses!
Le principal avantage de la création de petits systèmes découplés est sans aucun doute la testabilité ! Et c'est ce qui m'a aidé à créer une alternative de démonstration des écrans existants de l'application!
Un guide étape par étape
Maintenant, comment pourrions-nous appliquer ces principes à une application à flotteur?
Nous partagerons quelques techniques que nous utilisons pour construire Gestionnaire de tâches UDN[dix]avec un simple exemple de pas à pas.
L'exemple est si simple que cela ne permet pas d'éclairer tous les avantages derrière cela, mais croyez-moi, cela vous aidera à créer beaucoup plus d'applications de flottaison de maintenance avec des codes complexes. 💡
L'application
Par exemple, nous créerons une application qui affiche la population des États-Unis pour chaque année.
via Gestionnaire de tâches UDN[dix]
Nous avons deux écrans ici:
Nous allons nous concentrer sur le Mise en œuvre car c'est le plus intéressant avec son appel asynchrone.
Étape 1. Approche naïve
La mise en œuvre la plus évidente pour notre application est en utilisant un seul pour toute la logique.
Pour accéder à l'année demandée, nous lisons la du widget hérité.
Cet exemple invoque le fonction de la paquet pour obtenir les données de la datausa.io API , analyse le JSON résultant avec le méthode de la bibliothèque et garder Dans le cadre de l'état avec une propriété nommée .
Pour créer l'arborescence du widget, nous utilisons un , qui se reconstruit sur l'état actuel de notre appel asynchrone.
D'accord, la mise en œuvre est courte et utilise uniquement des widgets intégrés, mais pense maintenant à notre intention initiale: construire des alternatives de démonstration (ou tests) pour cet écran. Il est très difficile de contrôler le résultat de l'appel HTTP pour forcer l'application à rendre dans un certain état.
C'est là que le concept de inversion de contrôle va nous aider. 🧐
Étape 2. Inversion du contrôle
Ce principe peut être difficile à comprendre pour les nouveaux développeurs ( et aussi difficile à expliquer ), mais l'idée générale est de extraire les préoccupations en dehors de nos composants -So qu'ils ne sont pas responsables du choix du comportement - et de le déléguer à la place.
Dans une situation plus fréquente, il consiste simplement à créer des abstractions et à injecter des implémentations dans nos composants afin que leur mise en œuvre puisse être modifiée ultérieurement si nécessaire.
Mais ne vous inquiétez pas, il sera plus logique après notre prochain exemple! 👀
Afin de contrôler l'appel HTTP à notre API, nous avons isolé notre mise en œuvre dans un dédié classer. Nous avons également créé un classe pour rendre les données plus faciles à manipuler et à entretenir.
Pour notre exemple, nous utilisons le bien connu fournisseur paquet pour injecter un par exemple à la racine de notre arbre.
Le fournisseur permet à tout widget descendant ( aimer notre ) lire le plus proche haut dans l'arbre. On peut alors utiliser son méthode pour commencer notre , À la place de la mise en œuvre HTTP réelle.
Maintenant, nous pouvons profiter de tout cela!
Si vous ne saviez pas: toutes les classes dans dart également définir implicitement une interface associée[dix]. Cela nous permet de fournir une implémentation alternative de qui renvoie toujours la même instance de son appelle la méthode.
Cette méthode
Nous avons maintenant toutes les clés pour afficher une instance de démonstration de la !
Nous remplaçons simplement le moment prévu par exemple en enveloppant notre dans un fournisseur qui crée un par exemple à la place!
Et que notre-est lit cette instance de démonstration à la place, et utilise notre données au lieu d'un appel HTTP.
Ceci est un excellent exemple de Inversion de contrôle. Notre widget est pas responsable de la logique d'obtenir les données plus, mais délègue la place à un objet client dédié. Et nous sommes maintenant en mesure de créer des instances de démonstration de l'écran, ou pour mettre en œuvre des tests pour notre widget écran! Impressionnant! 👏
Mais nous pouvons faire encore mieux!
Puisque nous ne sommes pas en mesure de simuler un état de chargement, par exemple, nous n'avons pas le plein contrôle de tout changement d'état à notre niveau widget.
Étape 3. Gestion de l'Etat
Ceci est un sujet brûlant dans Flutter!
Je suis sûr que vous avez déjà lu de longs fils de personnes qui tentent d'élire la meilleure gestion de l'Etat solution pour Flutter. Et pour être clair, ce n'est pas ce que nous allons faire dans cet article. À notre avis, tant que vous séparez votre logique métier de votre logique visuelle, vous allez bien! La création de ces couches est vraiment important pour maintenabilité. Notre exemple est simple, mais dans des applications réelles, la logique peut rapidement devenir complexe et cette séparation rend beaucoup plus facile de trouver vos algorithmes logiques pures. Ce sujet est souvent résumée comme la gestion de l'État .
Dans cet exemple, nous utilisons une base à côté d'une . Mais nous aurions pu aussi utilisé flutter_bloc ou riverpod (Ou une autre solution) , Et il aurait fonctionnait très bien, aussi. Les principes restent les mêmes, et aussi longtemps que vous et vos états séparés votre logique, il est même possible de transférer votre base de code de l'une des autres solutions.
Cette séparation nous permet également de contrôler tout état de nos widgets afin que nous puissions il se moquer de toutes les manières possibles!
Au lieu de compter sur la du cadre, nous représentons maintenant notre état d'écran comme objet.
Il est également important de mettre en œuvre la et méthodes pour rendre notre objet comparable en valeur. Cela nous permet de détecter si deux instances doivent être considérés comme différents.
Le 💡 assimilables ou freezed les paquets sont d'excellentes options pour vous aider à mettre en œuvre la et méthodes!
Notre état est toujours associé à une , Mais nous avons aussi quatre états distincts possibles sur ce que nous voulons montrer à l'utilisateur:
Ces états sont plus clairs qu'une , Car il représente vraiment nos cas d'utilisation. Et encore une fois, ce qui rend notre code plus maintenable!
💡 Nous vous recommandons vivement la types Union du paquet freezed pour représenter vos états logiques! Il ajoute beaucoup de services publics comme la ou méthodes.
Maintenant que nous avons une représentation de notre état, nous devons stocker une instance de quelque part qui est le but du . Il gardera le courant par exemple dans sa la propriété et fournira des méthodes pour mettre à jour l'état.
Nous fournissons une état initial, et un Procédé pour charger des données à partir de la et mettre à jour le courant .
Pour instancier et observer l'état de notre écran, nous comptons également sur le fournisseur et son . Ce type de fournisseur recherche automatiquement pour tout créé et déclenchera une reconstruction de la chaque fois qu'il est informé d'un changement (Lorsque notre valeur est différente de notification précédent).
Génial! Notre architecture de l'application commence à sembler très bon. Tout est séparé en couches bien définies, avec des préoccupations spécifiques! 🤗
Une chose est toujours manquante pour la qualification, nous voulons définir le courant contrôler l'état de notre associé .
Étape 4. Widget de mise en page dédié visuel
Au cours de la dernière étape, nous avons donné un peu trop de responsabilité envers notre widget: il était responsable de l'instanciation de la . Et comme ce que nous avons vu précédemment, nous essayons d'éviter toute responsabilité logique à la couche de vue!
Nous pouvons facilement résoudre cela en créant une autre couche pour notre widget d'écran: nous allons diviser notre widget en deux:
En combinant les deux, nous pourrons simplement créer démo / instances de test, mais ayant Pour le cas d'utilisation réelle dans notre application.
Pour obtenir une meilleure séparation des préoccupations, nous avons tout déplacé sous le widget à un dédié widget. Ce nouveau widget ne consomme que des données et n'est pas responsable de l'instanciation. Il convertit simplement l'état de lecture à un arbre de widget spécifique.
le appeler et l'instance reste dans le et ce widget renvoie simplement le avec un arbre de dépendance préconfiguré!
Cette amélioration mineure est spécifique à l'utilisation des fournisseurs, mais vous remarquerez que nous avons également ajouté un afin que tout widget descendant puisse directement consommer un . Cela facilitera la simulation de données.
N'hésitez jamais à extraire un arbre de widget en une classe dédiée! Cela améliorera les performances et rendra le code plus maintenu.
Dans notre exemple, nous avons créé un widget de mise en page visuelle pour chacun des types d'état associés:
Maintenant, nous avons le contrôle total sur ce que nous pouvons moquer et afficher sur notre écran!
Nous devons juste envelopper un avec un simuler l'état de la mise en page.
Conclusion
Créer une architecture logicielle maintenable n'est pas définitive pas facile! Anticiper les scénarios futurs peut exiger beaucoup d'efforts, mais j'espère que les rares conseils que j'ai partagés vous aideront à l'avenir!
Les exemples peuvent sembler simples - cela pourrait même sembler que nous sommes en train de sur-ingénierie, mais la complexité de votre application augmente, avoir ces normes vous aidera beaucoup! 💪
Amusez-vous avec Flutter et suivez le blog pour obtenir des articles plus techniques comme celui-ci! Restez à l'écoute!