Flutter : A Song of iOS and Android

  • posté le
  • par ESENS
  • Cela fait maintenant 8 ans que je développe des applications mobiles, tant sur Android que sur iOS. C'est vous dire si j'ai vu passer des frameworks multiplateformes sous mes yeux : Cordova, Xamarin, React Native, etc. J'ai toujours été circonspect vis à vis de ces techniques d'apprentis sorciers mais je me suis aussi toujours intéressé de près à leur évolution et surtout, à leur potentielle plus-value.  

Si on tend souvent à perdre en performance, en qualité de code, et surtout, en cohésion avec les obligations d’affichage de chaque plateforme, il reste cependant indéniable que pour un premier jet, ce type de framework reste une bonne base pour tester un futur produit. Et au risque de m’attirer les foudres de mes pairs, j’avoue avoir toujours porté un vif intérêt pour ces technologies cross-platform. 


C’est pour cette raison qu'à l'annonce du lancement de Flutter, je me suis jeté sur la documentation pour découvrir ce nouvel outil.

Made by Google, est-il expliqué sur le site officiel. What could go wrong ?

Au premier abord, Google nous présente évidemment son outil comme révolutionnaire. Des UI faciles à mettre en place, une exploitation intuitive des performances de chaque plateforme, une communauté grandissante autour d’un langage créé par les ingénieurs de Google. Aie. Premier bémol.

Si vous voulez démarrer sur Flutter, vous allez devoir apprendre un nouveau langage : Dart. Fort heureusement, en 11 ans d’existence, la communauté a essuyé la plupart des plâtres que peut rencontrer un premier langage.  

Plutôt qu’un discours déjà trop long, voici un petit exemple technique pour découvrir l’outil.

 

Un peu de technique
 

La première chose à faire lorsque l'on découvre Flutter, c'est d'implémenter une petite interface utilisateur.

Quelques éléments de base à connaitre concernant l'UI Flutter :

  • Tous les éléments affichables sont des widgets.
  • Les éléments de base sont les Row et les Columns qui nous permettront d'aligner les widgets sur l'écran. On peut considérer ces deux widgets comme les fameuses <div> de l'HTML.
  • Ces éléments s'affichent dans des conteneurs (eux aussi considérés comme des widgets) de deux sortes :

    •     Les StateLessWidget : Une fois chargés et affichés, ces widget ne pourront plus changer à moins d'un rafraîchissement complet de l'interface.

    •     Les StateFulWidget : Ces widgets sont ceux avec lesquels l'utilisateur peut interagir. En fonction de cela, leur état (State) va changer et l'interface s'en verra modifiée.

Basons nous sur l'exemple fourni par le site officiel pour commencer. Ce StatelessWidget affiche un simple Hello World, au centre de l'écran : 

Ce StatefulWidget possède un bouton qui permet d'incrémenter une variable affichée à l'écran :

Il est intéressant de noter que chaque clic sur le bouton modifie la variable seulement. Le widget Text n'est pas modifié directement. Au lieu de cela, Flutter implémente un pattern observer et notifie le widget de la modification.

Un second point intéressant réside dans le fast reload que permet Flutter. Modifiez par exemple la String contenu dans le widget Text, sauvegardez, et l'interface sera immédiatement (ou presque) mise à jour sur votre device de test.

En terme d'intégration, c'est un avantage indéniable.

Ce petit morceau de code fait uniquement appel à la partie Material de Flutter. Résultat : la version iOS ressemble fortement à une application Android. Voila de quoi rendre les utilisateurs confus. Fort heureusement, plusieurs librairies proposent de palier à ce problème avec une simple surcouche.

Voici celle que j'utilise. Remplacez vos Button par PlatformButton et bénéficiez directement du style propre à chaque OS. Magique, non ?

Ces exemples sont très simples, mais nous ne sommes pas ici pour rédiger la documentation complète de Flutter. Je vous invite à vous rendre sur le site officiel pour découvrir l'énorme variété de composants disponibles.  


Intéressons-nous maintenant à la partie la plus intéressante : comment Flutter exploite-t-il les fonctionnalités natives de chaque OS ?

Le mécanisme utilisé est appelé 'Channel'. Il s'agit tout simplement de réflexions. Voilà de quoi hérisser le poil de plus d'un développeur. Mais il faut parfois faire des concessions et si l'on s'y intéresse un peu, on réalise vite que ce système n'est finalement pas si désagréable à utiliser.

Tout d'abord, faisons la part des choses. La partie émérgée de l'iceberg, c'est à dire votre code Dart pur, est déjà capable de beaucoup de choses (accès réseau, gestion de firebase, gestion du cycle de vie des Activity/ViewControllers, etc). Finalement, si vous devez utiliser les Channel, il s'agira surtout d'utiliser des fonctions très spécifiques du smartphone (Bluetooth, Wifi, accéleromètre, base de données comme realm ou room et autres).

Prenons un exemple simple et disons que nous souhaiterions sauvegarder le nom de notre utilisateur - j'avais dit simple ! Pour Android, on voudra surement utiliser les SharedPreferences. Pour iOS on pourra choisir NSUserDefault. 

Voici comment cela fonctionne :

Flutter crée un canal de communication avec les couches natives des deux OS (le fameux channel). Vous pouvez créer autant de canaux que vous le souhaitez. Ceux ci sont identifiés par une string arbitraire. Ce canal doit aussi être déclaré dans chaque couche native.

Flutter et les OS natifs ne peuvent communiquer qu'avec des types simples. En voici la liste :

Dart

Android

iOS

null

null

nil (NSNull when nested)

bool

java.lang.Boolean

NSNumber numberWithBool:

int

java.lang.Integer

NSNumber numberWithInt:

int, if 32 bits not enough

java.lang.Long

NSNumber numberWithLong:

double

java.lang.Double

NSNumber numberWithDouble:

String

java.lang.String

NSString

Uint8List

byte[]

FlutterStandardTypedData typedDataWithBytes:

Int32List

int[]

FlutterStandardTypedData typedDataWithInt32:

Int64List

long[]

FlutterStandardTypedData typedDataWithInt64:

Float64List

double[]

FlutterStandardTypedData typedDataWithFloat64:

List

java.util.ArrayList

NSArray

Map

java.util.HashMap

NSDictionary

Ci dessous un exemple d'appel aux couches natives

Coté Flutter :

Côté Android :

Côté iOS :

Il est clair que l’usage de reflection demande une grande rigueur concernant le nommage des méthodes et variables. Cet usage doit selon moi rester exceptionnel pour éviter au maximum les effets de bords. En attendant que l’on nous propose une meilleure solution, il faudra se contenter de cela.

Conclusion

Au final, que dire de Flutter ?

Après quelques heures d'expérimentation, je m'en tiens à ce que j'ai déjà écrit en introduction. Je pense qu'il s'agit ici d'un excellent outil pour créer des P.O.C ou de petites applications simples. J'émets quelques réserves sur le potentiel de rentabilité pour une application lourde. Je n'ai pas encore pu tester la couche réseau de manière très poussée et j'ai un peu peur des délais que peuvent engendrer les transferts de données via le Channel.

Mais tout n'est pas noir : si Google continue d'améliorer son concept, il est fort probable que Flutter devienne un outil très efficace et le site officiel met déjà en avant une pléthore d'applications développées avec ce framework.

Patientons donc encore un peu et, en attendant, continuons de mener notre guerre entre développeurs natifs ! ;) 

Article rédigé par Quentin, Lead Dev Mobile chez ESENS | Retrouvez tous nos articles sur le Blog ESENS


Vous êtes à la recherche d'un nouveau challenge ? Rejoignez l'équipe ESENS en postulant à nos offres d'emploi !

PARTAGER CET ARTICLE