Résolution des problèmes multiplateformes courants lors de l'utilisation de Flutter
Publié: 2022-03-10J'ai vu beaucoup de confusion en ligne concernant le développement Web avec Flutter et, souvent, c'est malheureusement pour de mauvaises raisons.
Plus précisément, les gens le confondent parfois avec les anciens frameworks multiplateformes mobiles (et de bureau) basés sur le Web, qui n'étaient essentiellement que des pages Web exécutées dans des navigateurs exécutés dans une application wrapper.
C'était vraiment multiplateforme dans le sens où les interfaces étaient de toute façon les mêmes car vous n'aviez accès qu'aux interfaces normalement accessibles sur le Web.
Flutter n'est pas cela, cependant : il fonctionne nativement sur chaque plate-forme, et cela signifie que chaque application fonctionne comme si elle était écrite en Java/Kotlin ou Objective-C/Swift sur Android et iOS, à peu près. Vous devez le savoir car cela implique que vous devez prendre en compte les nombreuses différences entre ces plateformes très diverses.
Dans cet article, nous allons voir certaines de ces différences et comment les surmonter. Plus précisément, nous allons parler des différences de stockage et d'interface utilisateur, qui sont celles qui causent le plus souvent de la confusion aux développeurs lors de l'écriture de code Flutter qu'ils souhaitent être multiplateforme.
Exemple 1 : Stockage
J'ai récemment écrit sur mon blog sur la nécessité d'une approche différente pour stocker les JWT dans les applications Web par rapport aux applications mobiles.
Cela est dû à la nature différente des options de stockage des plates-formes et à la nécessité de connaître chacune de leurs outils de développement natifs.
la toile
Lorsque vous écrivez une application Web, les options de stockage dont vous disposez sont :
- télécharger/télécharger des fichiers vers/depuis le disque, ce qui nécessite une interaction de l'utilisateur et ne convient donc qu'aux fichiers destinés à être lus ou créés par l'utilisateur ;
- utiliser des cookies, qui peuvent ou non être accessibles depuis JS (selon qu'ils sont ou non
httpOnly
) et sont automatiquement envoyés avec les requêtes à un domaine donné et enregistrés lorsqu'ils arrivent dans le cadre d'une réponse ; - en utilisant JS
localStorage
etsessionStorage
, accessibles par n'importe quel JS sur le site Web, mais uniquement à partir de JS qui fait partie des pages de ce site Web.
Mobile
La situation en ce qui concerne les applications mobiles est complètement différente. Les options de stockage sont les suivantes :
- documents d'application locaux ou stockage en cache, accessibles par cette application ;
- autres chemins de stockage locaux pour les fichiers créés/lisibles par l'utilisateur ;
-
NSUserDefaults
etSharedPreferences
respectivement sur iOS et Android pour le stockage clé-valeur ; -
Keychain
sur iOS etKeyStore
sur Android pour le stockage sécurisé de, respectivement, toutes les données et les clés cryptographiques.
Si vous ne savez pas cela, vous allez gâcher vos implémentations car vous devez savoir quelle solution de stockage vous utilisez réellement et quels en sont les avantages et les inconvénients.
Solutions multiplateformes : une première approche
L'utilisation du package Flutter shared_preferences
utilise localStorage
sur le Web, SharedPreferences
sur Android et NSUserDefaults
sur iOS. Celles-ci ont des implications complètement différentes pour votre application, en particulier si vous stockez des informations sensibles telles que des jetons de session : localStorage
peut être lu par le client, c'est donc un problème si vous êtes vulnérable à XSS. Même si les applications mobiles ne sont pas vraiment vulnérables à XSS, SharedPreferences
et NSUserDefaults
ne sont pas des méthodes de stockage sécurisées car elles peuvent être compromises côté client car elles ne sont pas un stockage sécurisé et ne sont pas chiffrées. C'est parce qu'ils sont destinés aux préférences de l'utilisateur, comme mentionné ici dans le cas d'iOS et ici dans la documentation Android en parlant de la bibliothèque de sécurité qui est conçue pour fournir des wrappers aux SharedPreferences
spécifiquement pour chiffrer les données avant de les stocker.
Stockage sécurisé sur mobile
Les seules solutions de stockage sécurisées sur mobile sont respectivement Keychain
et KeyStore
sur iOS et Android, alors qu'il n'existe pas de stockage sécurisé sur le Web .
Le Keychain
et KeyStore
sont cependant de nature très différente : le Keychain
est une solution générique de stockage d'informations d'identification, tandis que le KeyStore
est utilisé pour stocker (et peut générer) des clés cryptographiques , qu'il s'agisse de clés symétriques ou de clés publiques/privées.
Cela signifie que si, par exemple, vous avez besoin de stocker un jeton de session, sur iOS, vous pouvez laisser le système d'exploitation gérer la partie cryptage et simplement envoyer votre jeton au Keychain
, alors que sur Android, c'est un peu plus une expérience manuelle car vous avez besoin pour générer (pas de codage en dur, c'est mauvais) une clé, utilisez-la pour chiffrer le jeton, stockez le jeton chiffré dans SharedPreferences
et stockez la clé dans le KeyStore
.
Il existe différentes approches à cela, comme la plupart des choses en matière de sécurité, mais la plus simple consiste probablement à utiliser le chiffrement symétrique, car il n'y a pas besoin de chiffrement à clé publique puisque votre application chiffre et déchiffre le jeton.
De toute évidence, vous n'avez pas besoin d'écrire de code spécifique à la plate-forme mobile qui fait tout cela, car il existe un plugin Flutter qui fait tout cela, par exemple.
Le manque de stockage sécurisé sur le Web
C'est d'ailleurs la raison qui m'a poussé à écrire ce post. J'ai écrit sur l'utilisation de ce package pour stocker JWT sur des applications mobiles et les gens voulaient la version Web de cela mais, comme je l'ai dit, il n'y a pas de stockage sécurisé sur le Web . Il n'existe pas.
Cela signifie-t-il que votre JWT doit être ouvert ?
Non pas du tout. Vous pouvez utiliser des cookies httpOnly
, n'est-ce pas ? Ceux-ci ne sont pas accessibles par JS et sont envoyés uniquement à votre serveur. Le problème avec cela est qu'ils sont toujours envoyés à votre serveur, même si l'un de vos utilisateurs clique sur une URL de demande GET sur le site Web de quelqu'un d'autre et que cette demande GET a des effets secondaires que vous ou votre utilisateur n'aimerez pas. Cela fonctionne également pour d'autres types de requêtes, c'est juste plus compliqué. Cela s'appelle Cross-Site Request Forgery et vous ne voulez pas cela. Il fait partie des menaces de sécurité Web mentionnées dans les documents MDN de Mozilla, où vous pouvez trouver une explication plus complète.
Il existe des méthodes de prévention. Le plus courant est d'avoir deux jetons, en fait : l'un d'eux atteignant le client en tant que cookie httpOnly
, l'autre dans le cadre de la réponse. Ce dernier doit être stocké dans localStorage
et non dans des cookies car nous ne voulons pas qu'il soit envoyé automatiquement au serveur.
Résoudre les deux
Et si vous aviez à la fois une application mobile et une application Web ?
Cela peut être traité de l'une des deux manières suivantes :
- Utilisez le même point de terminaison principal, mais récupérez et envoyez manuellement les cookies à l'aide des en-têtes HTTP liés aux cookies ;
- Créez un point de terminaison principal non Web distinct qui génère un jeton différent de celui utilisé par l'application Web, puis autorisez une autorisation JWT régulière si le client est en mesure de fournir le jeton mobile uniquement.
Exécuter un code différent sur différentes plates-formes
Voyons maintenant comment nous pouvons exécuter différents codes sur différentes plates-formes afin de pouvoir compenser les différences.
Création d'un plugin Flutter
Surtout pour résoudre le problème du stockage, vous pouvez le faire avec un package de plug-ins : les plug-ins fournissent une interface Dart commune et peuvent exécuter différents codes sur différentes plates-formes, y compris le code natif spécifique à la plate-forme Kotlin/Java ou Swift/Objective-C . Développer des packages et des plugins est assez complexe, mais il est expliqué à de nombreux endroits sur le Web et ailleurs (par exemple dans les livres Flutter), y compris la documentation officielle de Flutter.

Pour les plates-formes mobiles, par exemple, il existe déjà un plugin de stockage sécurisé, et c'est flutter_secure_storage
, pour lequel vous pouvez trouver un exemple d'utilisation ici, mais cela ne fonctionne pas sur le Web, par exemple.
D'autre part, pour un stockage clé-valeur simple qui fonctionne également sur le Web, il existe un package de plug-in multiplateforme développé par Google appelé shared_preferences
, qui possède un composant spécifique au Web appelé shared_preferences_web
qui utilise NSUserDefaults
, SharedPreferences
ou localStorage
selon la plateforme.
Plate-forme cible sur Flutter
Après avoir importé package:flutter/foundation.dart
, vous pouvez comparer Theme.of(context).platform
aux valeurs :
-
TargetPlatform.android
-
TargetPlatform.iOS
-
TargetPlatform.linux
-
TargetPlatform.windows
-
TargetPlatform.macOS
-
TargetPlatform.fuchsia
et écrivez vos fonctions de sorte que, pour chaque plate-forme que vous souhaitez prendre en charge, elles fassent la chose appropriée. Cela sera particulièrement utile pour le prochain exemple de différence de plate-forme, et c'est les différences dans la façon dont les widgets sont affichés sur différentes plates-formes.
Pour ce cas d'utilisation, en particulier, il existe également un plug-in flutter_platform_widgets
raisonnablement populaire, qui simplifie le développement de widgets compatibles avec la plate-forme.
Exemple 2 : Différences dans l'affichage d'un même widget
Vous ne pouvez pas simplement écrire du code multiplateforme et prétendre qu'un navigateur, un téléphone, un ordinateur et une smartwatch sont la même chose - à moins que vous ne vouliez que votre application Android et iOS soit une WebView et que votre application de bureau soit construite avec Electron . Il y a de nombreuses raisons de ne pas le faire, et ce n'est pas le but de cet article de vous convaincre d'utiliser à la place des frameworks comme Flutter qui gardent votre application native, avec tous les avantages de performances et d'expérience utilisateur qui vont avec, tout en vous permettant de écrire du code qui sera le même pour toutes les plates-formes la plupart du temps.
Cela nécessite cependant des soins et de l'attention, et au moins une connaissance de base des plates-formes que vous souhaitez prendre en charge, de leurs API natives réelles, etc. Les utilisateurs de React Native doivent y prêter encore plus d'attention, car ce framework utilise les widgets intégrés du système d'exploitation. Vous devez donc faire encore plus attention à l'apparence de l'application en la testant de manière approfondie sur les deux plates-formes, sans pouvoir basculer Widget iOS et Material à la volée comme c'est possible avec Flutter.
Ce qui change sans votre demande
Certains aspects de l'interface utilisateur de votre application sont automatiquement modifiés lorsque vous changez de plate-forme. Cette section mentionne également ce qui change entre Flutter et React Native à cet égard.
Entre Android et iOS (Flutter)
Flutter est capable de rendre les widgets Material sur iOS (et les widgets Cupertino (de type iOS) sur Android), mais ce qu'il NE FAIT PAS, c'est d'afficher exactement la même chose sur Android et iOS : le thème Material s'adapte particulièrement aux conventions de chaque plate-forme. .
Par exemple, les animations de navigation, les transitions et les polices par défaut sont différentes, mais elles n'ont pas beaucoup d'impact sur votre application.
Ce qui peut affecter certains de vos choix en matière d'esthétique ou d'UX, c'est le fait que certains éléments statiques changent également. Concrètement, certaines icônes changent entre les deux plateformes, les titres de la barre d'application sont au milieu sur iOS et à gauche sur Android (à gauche de l'espace disponible au cas où il y aurait un bouton retour ou le bouton pour ouvrir un tiroir (expliqué ici dans les directives de conception de matériaux et également connu sous le nom de menu hamburger). Voici à quoi ressemble une application Material avec un tiroir sur Android :

Et à quoi ressemble la même application Material, très simple, sur iOS :

Entre mobile et Web et avec encoches d'écran (Flutter)
Sur le Web, la situation est un peu différente, comme mentionné également dans cet article Smashing sur le développement Web réactif avec Flutter : en particulier, en plus de devoir optimiser pour des écrans plus grands et de tenir compte de la façon dont les gens s'attendent à naviguer sur votre site - qui est l'objectif principal de cet article - vous devez vous soucier du fait que parfois les widgets sont placés en dehors de la fenêtre du navigateur. De plus, certains téléphones ont des encoches dans la partie supérieure de leur écran ou d'autres obstacles à la visualisation correcte de votre application en raison d'une sorte d'obstruction.
Ces deux problèmes peuvent être évités en enveloppant vos widgets dans un widget SafeArea
, qui est un type particulier de widget de rembourrage qui garantit que vos widgets tombent à un endroit où ils peuvent réellement être affichés sans que rien n'entrave la capacité des utilisateurs à les voir, qu'il s'agisse d'une contrainte matérielle ou logicielle.
En réaction native
React Native nécessite beaucoup plus d'attention et une connaissance beaucoup plus approfondie de chaque plateforme, en plus de vous obliger à faire tourner le simulateur iOS ainsi que l'émulateur Android à minima afin de pouvoir tester votre application sur les deux plateformes : ce n'est pas identique et il convertit ses éléments d'interface utilisateur JavaScript en widgets spécifiques à la plate-forme. En d'autres termes, vos applications React Native ressembleront toujours à iOS - avec les éléments de l'interface utilisateur Cupertino comme on les appelle parfois - et vos applications Android ressembleront toujours aux applications Android Material Design classiques car elles utilisent les widgets de la plate-forme.
La différence ici est que Flutter rend ses widgets avec son propre moteur de rendu de bas niveau, ce qui signifie que vous pouvez tester les deux versions de l'application sur une seule plate-forme.
Contourner ce problème
À moins que vous ne recherchiez quelque chose de très spécifique, votre application est censée avoir un aspect différent sur différentes plates-formes, sinon certains de vos utilisateurs seront mécontents.
Tout comme vous ne devriez pas simplement expédier une application mobile sur le Web (comme je l'ai écrit dans le post Smashing susmentionné), vous ne devriez pas expédier une application pleine de widgets Cupertino aux utilisateurs d'Android, par exemple, car cela va être déroutant pour la plus grande partie. D'un autre côté, avoir la possibilité d'exécuter une application contenant des widgets destinés à une autre plate-forme vous permet de tester l'application et de la montrer aux personnes dans les deux versions sans avoir à utiliser nécessairement deux appareils pour cela.
L'autre côté : utiliser les mauvais widgets pour les bonnes raisons
Mais cela signifie également que vous pouvez effectuer la majeure partie de votre développement Flutter sur un poste de travail Linux ou Windows sans sacrifier l'expérience de vos utilisateurs iOS, puis simplement créer l'application pour l'autre plate-forme sans avoir à vous soucier de la tester en profondeur.
Prochaines étapes
Les frameworks multiplateformes sont géniaux, mais ils vous transfèrent la responsabilité, en tant que développeur, de comprendre le fonctionnement de chaque plate-forme et de vous assurer que votre application s'adapte et est agréable à utiliser pour vos utilisateurs. D'autres petites choses à considérer peuvent être, par exemple, l'utilisation de différentes descriptions pour ce qui pourrait être essentiellement la même chose s'il existe différentes conventions sur différentes plates-formes.
C'est formidable de ne pas avoir à créer les deux applications (ou plus) séparément en utilisant des langues différentes, mais vous devez toujours garder à l'esprit que vous créez essentiellement plus d'une application et que cela nécessite de réfléchir à chacune des applications que vous construisez. .
Autres ressources
- Le site Web Flutter Gallery et l'application Android, présentant l'utilisation de widgets Flutter typiques de différentes plates-formes et leur agnosticisme de plate-forme
- Documentation de l'API Flutter sur TargetPlatform
- Documentation Flutter sur la création de packages et plugins
- Documentation Flutter sur les adaptations de plateforme
- Documentation MDN sur les cookies