Une introduction aux concepts et à la mise en œuvre
Les concepteurs et développeurs Web ont été battus au-dessus de la tête en parlant de conception réactive. Il ne s’agit pas vraiment de décider s’il faut mettre en œuvre des principes réactifs, mais jusqu’où aller. Google récompense les sites Web pour leur réactivité dans leurs classements de recherche. Plus important encore, nos téléspectateurs/utilisateurs/clients vous récompenseront pour une bonne conception sur l’appareil de leur choix en étant plus heureux et plus engagés. Nous aimons tous être heureux, n’est-ce pas ?
Flutter est immature en ce qui concerne l’utilisation du Web (en fait, par défaut, il utilise une méthode de rendu que les robots Web ne pourraient pas juger de la réactivité). Cependant, le Web n’est pas la seule plate-forme où la conception réactive est importante. Les applications de bureau ont autant de considérations d’écran que le Web. Les appareils mobiles comme les petits téléphones, les pliables et les tablettes massives consomment les mêmes applications que Flutter peut créer. Même si une application est limitée au mode portrait sur les iPhones, une conception réactive peut faire la différence entre un comportement magnifique ou une rupture catastrophique.
Tout d’abord, nous résumerons quelques principes de conception réactive. Ensuite, nous ferons deux tentatives d’implémentation réactive : une implémentation vanille naïve et une utilisant une bibliothèque.
Un design est responsive quand il répond aux changements de son environnement. Nous parlerons spécifiquement de la taille et de l’orientation de l’écran, mais de manière réaliste, les applications peuvent répondre à toutes sortes de configurations d’appareils différentes (mode d’accessibilité activé, aperçu avant impression, entrées tactiles/périphériques).
Le design n’est pas une solution unique. Inévitablement, il y aura du contenu, des images et des mises en page qui seront superbes sur un appareil de la taille d’un ordinateur de bureau et terribles sur un téléphone mobile (et vice versa). Nous voulons que notre application réponde à ces différents scénarios avec les changements appropriés qui conservent autant de fonctionnalités que possible, préservent l’accessibilité et rendent les gens heureux. La manière d’y parvenir est laissée au processus de conception, mais elle se décompose généralement en deux catégories : conception adaptative et fluide.
Conception adaptative
La conception adaptative joue assez librement dans sa définition, selon à qui nous demandons. La base est de créer des conceptions spécifiquement pour un ensemble fini d’écrans. Cela signifie que sur la multitude de variantes d’écran, les utilisateurs ne verront qu’un seul design de l’ensemble prédéfini – celui qui convient le mieux à leur appareil.
Nous le voyons sur certains sites Web en faisant glisser la fenêtre plus petite et rien ne change – jusqu’à ce qu’il le fasse. À différents seuils de taille d’écran, appelés points d’arrêt, la conception « apparaîtra » à une autre qui convient mieux aux dimensions, en restant cohérente jusqu’à ce qu’elle « apparaisse » à nouveau si nécessaire. Ceci est avantageux en raison d’une réduction des actifs de conception et des tests nécessaires. Cependant, cela conduit à un espace « gaspillé » car la conception change de manière préventive pour s’adapter à l’écran et remplira rarement (uniquement si la taille de l’écran correspond à ces points d’arrêt) l’écran entièrement.
Conception fluide
La conception fluide « coule » en temps réel au fur et à mesure que l’écran change, remplissant souvent l’écran ou un certain pourcentage de celui-ci. L’échelle des éléments, les retours à la ligne et la disposition générale peuvent changer d’une manière ou d’une autre chaque fois que les dimensions de l’écran sont modifiées. Bien que cela rende la conception beaucoup plus difficile (le nombre de scénarios de taille d’écran est effectivement infini), cela signifie que nous pouvons avoir une expérience unique qui tire pleinement parti de l’appareil de l’utilisateur à tout moment.
La réalité
De nombreuses conceptions tirent parti à la fois de la conception adaptative et fluide. Nous pourrions constater que sur des tailles d’écran plus grandes, une application se comporte de manière adaptative, changeant entre les mises en page de taille fixe, tandis que sur des appareils plus petits, la conception devient fluide. Prenez la mise en page des articles de Medium, par exemple. Ils ont une mise en page apparemment statique sur des écrans plus grands, mais lorsqu’ils sont plus petits, le texte se remplit horizontalement et s’enroule à chaque largeur unique.
La plupart des applications pratiquent des modifications de conception réactives lorsque la largeur de l’écran s’ajuste.
« Hé, cet article est censé parler de Flutter ! » Vous avez raison. La conception réactive est intrinsèquement un problème de conception d’abord et un problème de mise en œuvre technique ensuite. Nous supposerons que nos concepteurs ont tout pris en compte et nous ont fourni des conceptions et des spécifications qui couvrent tous nos types d’appareils pris en charge – commençons.
Flutter est livré avec quelques utilitaires de base pour créer une implémentation réactive. La principale information que nous devons connaître pour répondre aux différentes tailles d’écran est… la taille de l’écran. Flutter nous fournit le MediaQuery
widget qui nous permet de « demander » des informations sur le « média » (appareil) sur lequel notre application s’exécute. Idéalement, ce widget est fourni prêt à l’emploi par un MaterialApp
, CupertinoApp
ou alors WidgetsApp
. Tout ce que nous avons à faire est de rechercher le MediaQueryData
objet et vérifier son size
propriété de n’importe où nous avons accès à un BuildContext
. Essayons:
final screenWidth = MediaQuery.of(context).size.width;
Maintenant que nous savons comment accéder à la taille de l’écran, comment réagissons-nous aux changements ? Appel MediaQuery.of(context)
à l’intérieur d’un widget build
méthode le marque comme dépendant d’un ancêtre MediaQuery
widget, le faisant reconstruire chaque fois que le retour MediaQueryData
changements d’objet. Nous nous soucions des modifications apportées à size
, qui se produisent lors d’événements tels que la manipulation de la fenêtre sur le Web/le bureau ou l’épinglage d’une application/le changement entre portrait et paysage sur un appareil mobile. Parce que nous avons la taille de l’écran, nous pouvons prendre des décisions qui affectent les widgets construits par nos méthodes de construction et créer des mises en page réactives.
Voici ce que nous allons construire.
Cet exemple artificiel montre plusieurs choses :
- Le texte du titre change de style
- Conception adaptative sur des écrans plus grands
- Conception fluide sur des écrans plus petits
Pour les points d’arrêt, gardons un modèle commun utilisé dans les bibliothèques Web comme Bootstrap : moyen (768px), large (992px), extra large (1200px) et extra extra large (1400px). Notez qu’aux grandes tailles, le contenu se trouve dans un conteneur fixe et s’ajuste rarement (adaptatif), tandis qu’aux petites tailles, le contenu remplit la largeur disponible à tout moment (fluide). Les tailles du conteneur dans les plages adaptatives suivent à nouveau une norme Web commune de large (960px), extra large (1140px) et extra extra large (1320px). Un concepteur devrait idéalement fournir des détails comme ceux-ci.
S’appuyant sur notre MediaQuery
exemple ci-dessus, examinons chacune des tâches à puces. Nous avons besoin d’une logique conditionnelle simple pour utiliser le style de texte de titre approprié pour la taille d’écran actuelle. Nous utiliserons un late final
variable et affectez-lui un style pour éviter une opération ternaire profondément imbriquée.
Nous obtenons d’abord la largeur de l’écran en utilisant MediaQuery
. Ensuite, nous vérifions cela par rapport à nos points d’arrêt définis par la conception et attribuons le style de texte approprié au headlineStyle
variable. Enfin, nous utilisons ce style dans notre Text
widget retourné par la méthode build. Simple, non ?
Cependant, nous pouvons déjà voir qu’il y a un développement inquiétant de passe-partout. Avec un plus grand nombre de points d’arrêt ou plus de widgets qui y répondent, cette condition peut devenir difficile à manier.
Ensuite, essayons notre main avec le conteneur adaptatif.
La solution ici est similaire à notre code de style de texte ci-dessus. Nous utilisons un haut-centre-aligné ConstrainedBox
avec des contraintes strictes sur les écrans plus grands et aucune contrainte sur les plus petits (ce qui lui permet de remplir la largeur disponible de manière fluide). Les lecteurs aux yeux d’aigle peuvent remarquer que si certains points d’arrêt sont partagés avec le code de style de texte, tous ne le sont pas.
Si nous devions combiner les deux structures conditionnelles, nous aurions besoin de code répété dans le ifs
qui sont exclusifs pour éviter de ne pas initialiser notre late
variables. L’alternative est de garder les structures conditionnelles séparées et de dupliquer les conditions, ce qui peut aider à la lisibilité mais sacrifier la brièveté.
Enfin, créons la section des cartes qui affiche 4, 2 ou 1 cartes par rangée à mesure que l’écran se rétrécit.
Dans ce scénario, nous allons au-delà du choix de valeurs simples et passons à la création d’arbres de widgets variés basés sur des points d’arrêt. Nous utilisons une série de Row
widgets avec nos cartes emballées Expanded
pour s’assurer qu’ils divisent l’espace uniformément. À notre plus petit point d’arrêt, nous renonçons au Row
et permettre le Column
pour disposer les cartes. Un modèle courant dans Flutter consiste à commencer à extraire des widgets dans de nouvelles classes lorsque l’imbrication des arbres devient trop profonde. Cela devient encore plus important avec potentiellement plusieurs arborescences pour tenir compte de diverses mises en page dans une seule fonction de construction.
Découvrez le code complet et les exemples combinés ci-dessus dans le VanillaResponsive
classe dans le exemple d’application.
Heureusement, la mise en œuvre réactive peut devenir beaucoup plus facile lorsque vous utilisez une bibliothèque qui répond à nos besoins. Il existe de nombreux excellents packages pub.dev qui traitent de la réactivité dans Flutter. En les recherchant pour mon propre usage, j’ai trouvé le besoin d’une boîte à outils complète avec une bonne sémantique et une bonne extensibilité. Alors sans vergogne, nous terminerons cet article en refaisant l’exercice ci-dessus en utilisant un package que j’ai développé et maintenu : responsive_toolkit.
Ce package nous donne les outils pour choisir de manière triviale entre plusieurs valeurs et mises en page à l’aide de points d’arrêt prédéfinis et nommés qui sont facilement étendus si nécessaire. De plus, un modèle de conception et de mise en page commun utilisant une grille à 12 colonnes est prêt à l’emploi. L’exemple suivant tire parti de ces fonctionnalités.
Par rapport à notre tentative précédente, la simplicité de ce code démontre la valeur de l’abstraction des complexités réactives dans une bibliothèque. Nous ne dépendons plus directement deMediaQuery
, et nous n’utilisons pas non plus de conditionnels qui réduisent la lisibilité. LeResponsiveLayout.value
Le constructeur clarifie les valeurs appartenant à chaque point d’arrêt et interprète intelligemment les valeurs des points d’arrêt non spécifiés de manière ascendante. Par exemple, les contraintes de xs
sont utilisés jusqu’à lg
pour le conteneur malgré non sm
ou alors md
.
Chaque carte est maintenant un enfant de ResponsiveColumn
dans unResponsiveRow
et partage une configuration réactive qui détermine le nombre de colonnes d’une grille de 12 colonnes qu’elle couvre. Cela a éliminé le besoin de construire plusieurs arbres de widgets différents et a gardé notre code plus propre, bien que le responsive_toolkit fournit également des outils pour les mises en page de widgets.
Découvrez le code complet dans le LibraryResponsive
classe dans le exemple d’application.
N’hésitez pas à consulter l’intégralité documentation pour responsive_toolkit et ses exemples utiles. Utilisez toujours la bibliothèque la mieux adaptée à votre application, mais si vous utilisez responsive_toolkitn’hésitez pas à contribuer, à soulever des problèmes et à suggérer des améliorations.
La réactivité est cruciale pour la conception et le développement d’applications modernes. Lorsqu’elles sont correctement mises en œuvre, nous créons les meilleures expériences disponibles pour les appareils de nos utilisateurs. Alors que Flutter a une mentalité de laisser-faire qui offre beaucoup de flexibilité, l’utilisation d’une bibliothèque peut accélérer le développement et créer un code plus propre.