Récemment, j'ai appris que la prochaine
version de Flutter (1.9) était sortie , ce qui promet divers avantages, y compris une prise en charge précoce des applications Web.
Au travail, je développe des applications mobiles sur React Native, mais je regarde Flutter avec curiosité. Pour ceux qui ne sont pas au courant: sur Flutter, vous pouvez déjà créer des applications pour Android et iOS, la prise en charge des applications Web est en cours de préparation pour la sortie, et il est également prévu de prendre en charge le bureau.
Tel est «un anneau pour tout gouverner».
Après avoir réfléchi quelques jours sur le type d'application que vous pouvez essayer de faire, j'ai décidé de choisir une tâche avec un astérisque - de quoi avons-nous besoin de ces pistes bien usées? Balancez-vous sur le bureau et surmontez héroïquement les difficultés! Pour l'avenir, je dirai que presque aucune difficulté n'est apparue.
Under the cut - une histoire sur la façon dont j'ai résolu les tâches habituelles du programmeur React Native en utilisant les outils Flutter, ainsi que l'impression générale de la technologie.

En réfléchissant aux fonctionnalités de Flutter que je voudrais «toucher», j'ai décidé que dans ma candidature, il faudrait:
- demandes à l'API distante;
- transitions entre écrans;
- Animations de transition
- gestionnaire d'état - redux ou quelque chose de similaire.
Je ne sais pas comment faire un backend, j'ai donc décidé de rechercher une API ouverte tierce. En conséquence, je me suis installé sur cette ressource -
cours CBR en XML et JSON, API . Eh bien, ici, j'ai finalement décidé de la fonctionnalité de l'application: il y aura deux écrans, sur le principal il y a une liste de devises au taux CBR, lorsque vous cliquez sur un élément de la liste, nous ouvrons un écran avec des informations détaillées.
La préparation
Étant donné que la commande
flutter create
ne sait pas encore comment créer un projet pour Windows / Linux (seul Mac est actuellement pris en charge, utilisez l'indicateur
--macos
pour cela), vous devez utiliser
ce référentiel, où il existe un exemple préparé. Nous clonons le référentiel, prenons l'
example
dossier à partir de là, si nécessaire, renommez-le et continuez à travailler dessus.
Étant donné que la prise en charge des plates-formes de bureau est toujours en cours de développement, vous devez toujours effectuer un certain nombre de manipulations. Pour accéder aux fonctionnalités en cours de développement, exécutez dans le terminal:
flutter channel master flutter upgrade
De plus, vous devez indiquer à Flutter qu'il peut utiliser votre plateforme:
flutter config --enable-linux-desktop
ou
flutter config --enable-macos-desktop
ou
flutter config --enable-windows-desktop
Si tout s'est bien passé, alors en exécutant la commande
flutter doctor
vous devriez voir une sortie similaire:

Donc, le décor est prêt, le public dans la salle - nous pouvons commencer.
Disposition
La première chose qui attire votre attention après React Native est l'absence d'un langage de balisage spécial à la JSX. Flutter vous oblige à écrire le balisage et la logique métier dans
Dart . Au début, c'est ennuyeux: le look n'a rien à saisir, le code semble lourd, et même ces crochets sont à la fin du composant!
Par exemple, tels que:

Et ce n'est pas la limite! Cela vaut la peine d'en enlever un au mauvais endroit et un passe-temps agréable (non) vous est garanti.
De plus, en raison des particularités des composants de style dans Flutter, pour les gros composants, le retrait du bord gauche de l'éditeur augmente assez rapidement, et avec lui le nombre de crochets fermés.
Cette fonctionnalité est que dans les styles Flutter sont les mêmes composants (pour être plus précis - widgets).
Si dans React Native pour disposer trois boutons dans une rangée à l'intérieur de la
View
afin qu'ils répartissent uniformément l'espace du conteneur, il me suffit de spécifier
flexDirection: 'row'
pour la
View
dans les styles et d'ajouter
flex: 1
pour les boutons dans les styles, alors Flutter a un composant distinct
Row
pour organiser les éléments dans une ligne et une autre pour "extensibilité" d'un élément sur tout l'espace disponible:
Expanded
.
En conséquence, au lieu de
<View style={{height: 100, width:300, flexDirection: 'row'}}> <Button title='A' style={{flex:1}}> <Button title='B' style={{flex:1}}> <Button title='C' style={{flex:1}}> </View>
nous devons écrire comme ceci:
Container( height: 100, width: 300, child: Row( children: <Widget>[ Expanded( child: RaisedButton( onPressed: () {}, child: Text('A'), ), ), Expanded( child: RaisedButton( onPressed: () {}, child: Text('B'), ), ), Expanded( child: RaisedButton( onPressed: () {}, child: Text('C'), ), ), ], ), )
Plus verbeux, non?
Vous pouvez également ajouter un cadre aux bords arrondis à ce conteneur. Dans React Native, nous ajoutons simplement aux styles:
borderRadius: 5, borderWidth: 1, borderColor: '#ccc'
Dans Flutter, nous devons ajouter quelque chose comme ça aux arguments du conteneur:
decoration: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(5)), border: Border.all(width: 1, color: Color(0xffcccccc)) ),
En général, au début, mon balisage s'est transformé en d'énormes feuilles de code, dans lesquelles le diable se cassait la jambe. Cependant, tout n'est pas si mauvais.
Tout d'abord, les gros composants doivent bien sûr être décomposés - placés dans des widgets séparés ou au moins dans les méthodes de votre classe de widgets.
Deuxièmement, le plugin Flutter dans VS Code aide beaucoup - dans l'image ci-dessus, les commentaires sur les crochets sont signés par le plugin lui-même (et ils ne peuvent pas être supprimés), ce qui aide à ne pas se confondre entre crochets. De plus, des outils de formatage automatique - après une demi-heure, vous vous habituez à appuyer périodiquement sur
Ctrl+Shift+I
pour formater le code.
De plus, la syntaxe du langage Dart dans la deuxième édition est devenue beaucoup plus agréable, donc à la fin de la journée, j'ai déjà aimé l'utiliser. Insolite? Oui Mais pas désagréable.
Demandes d'API
Dans React Native, pour obtenir des données de certaines API, nous utilisons généralement la méthode
fetch
, qui nous renvoie
Promise
.
À Flutter, la situation est similaire. Après avoir regardé les exemples dans la documentation, j'ai ajouté le package http à
pubspec.yaml
(un analogue de
package.json
du monde JS) et j'ai écrit quelque chose comme ceci:
Future<http.Response> getAnything() { return http.get(URL); }
L'objet
Future
un sens très similaire à Promise, donc tout est assez transparent ici. Eh bien, pour sérialiser / désérialiser des objets json, vous pouvez utiliser le concept de classes de modèle avec des méthodes spéciales de
fromJSON
/
toJSON
. Vous pouvez en savoir plus à ce sujet dans la
documentation .
Transition entre écrans
Malgré le fait que je faisais une application de bureau, du point de vue de Flutter, il n'y a aucune différence sur la plate-forme sur laquelle elle tourne. Eh bien, c'est dans mon cas, il en est ainsi, en général - je ne sais pas. En fait, la fenêtre système dans laquelle l'application Flutter est lancée est le même écran qu'un smartphone.
La transition entre les écrans est assez banale: nous créons une classe de widgets d'écran, puis utilisons la classe
Navigator
standard.
Dans le cas le plus simple, cela pourrait ressembler à ceci:
RaisedButton( child: Text('Go to Detail'), onPressed: () { Navigator.of(context).push<void>(MaterialPageRoute(builder: (context) => DetailScreen())); }, )
Si votre application comporte plusieurs écrans, il est plus raisonnable de préparer d'abord un dictionnaire de routes, puis d'utiliser la méthode
pushNamed
. Un petit exemple de la documentation:
class NavigationApp extends StatelessWidget {
De plus, vous pouvez préparer une animation spéciale pour basculer entre les écrans et écrire quelque chose comme ceci:
Navigator.of(context).push<void>(ScaleRoute(page: DetailScreen()));
Ici
ScaleRoute
est une classe spéciale pour créer des animations de transition. De bons exemples de telles animations peuvent être trouvés
ici .
Gestion de l'État
Il arrive que nous ayons besoin d'accéder à certaines données de n'importe quelle partie de notre application. Dans React Native,
redux
souvent utilisé (sinon le plus souvent) à ces fins.
Pour Flutter, il existe un
référentiel qui montre des exemples d'utilisation de diverses architectures d'application - il y a Redux, MVC et MVU, et même ceux dont je n'ai jamais entendu parler auparavant.
Après avoir fouillé un peu dans ces exemples, j'ai décidé de m'arrêter sur
Provider
.
En général, l'idée est assez simple: nous créons une classe spéciale qui
ChangeNotifier
classe
ChangeNotifier
, dans laquelle nous allons stocker nos données, les mettre à jour en utilisant les méthodes de cette classe et les récupérer si nécessaire. Voir la
documentation du package pour plus de détails.
Pour ce faire, ajoutez le package
provider
à
pubspec.yaml
et préparez la classe Provider. Dans mon cas, cela ressemble à ceci:
import 'package:flutter/material.dart'; import 'package:rates_app/models/rate.dart'; class RateProvider extends ChangeNotifier { Rate currentrate; void setCurrentRate(Rate rate) { this.currentrate = rate; notifyListeners(); } }
Ici,
Rate
est ma classe de modèle de devise (avec le
name
champs, le
code
, la
value
, etc.),
currentrate
est le champ dans lequel la devise sélectionnée sera stockée et
setCurrentRate
est la méthode par laquelle la valeur de
currentrate
est
currentrate
.
Pour attacher notre fournisseur à l'application, nous changeons le code de la classe d'application:
@override Widget build(BuildContext context) { return ChangeNotifierProvider( builder: (context) => RateProvider(),
Voilà, maintenant si nous voulons enregistrer la devise sélectionnée, nous écrivons quelque chose comme ceci:
Provider.of<RateProvider>(context).setCurrentRate(rate);
Et si nous voulons obtenir la valeur stockée, alors ceci:
var rate = Provider.of<RateProvider>(context).currentrate;
Tout est assez transparent et sans passe-partout (contrairement à Redux). Bien sûr, peut-être que pour des applications plus complexes, tout se passera moins bien, mais pour ceux comme mon exemple, les vins purs.
Créer une application
En théorie, la commande
flutter build <platform>
est utilisée pour construire l'application. En pratique, lorsque j'ai exécuté la commande
flutter build linux
, j'ai reçu ce message:

"Cela n'a pas fait de mal", pensais-je, j'ai été horrifié par le poids du dossier de
build
- 287,5 Mo - et à cause de la simplicité de mon âme, j'ai supprimé ce dossier pour toujours. Il s'est avéré - en vain.
Après avoir supprimé le répertoire de
build
, le projet a cessé de démarrer. Je n'ai pas pu le restaurer, je l'ai donc copié à partir de l'exemple d'origine. Cela n'a pas aidé - le collectionneur a juré sur les fichiers manquants.
Après avoir effectué quelques recherches, il s'est avéré que dans ce dossier il y avait un fichier
snapshot_blob.bin.d
, qui, apparemment, contient les chemins d'accès à tous les fichiers utilisés dans le projet. J'ai ajouté les chemins manquants et cela a fonctionné.
Ainsi, pour le moment, Flutter ne sait pas comment préparer les versions de version pour le bureau. Quoi qu'il en soit, pour Linux.
En général, si vous fermez les yeux sur ce moins, l'application s'est avérée comme je le voulais et ressemble
Bonus
Nous passons au bonus promis.
Même au stade de la rédaction de l'application, j'avais envie de vérifier à quel point il serait difficile de la porter sur d'autres plateformes. Commençons par le téléphone portable.
Il y a sûrement un moyen moins barbare, mais j'ai décidé que le chemin le plus court est direct. Par conséquent, j'ai simplement créé un nouveau projet Flutter, transféré le fichier
pubspec.yaml
, les
assets
, les
fonts
et les répertoires
lib
et y ajouté la ligne dans
AndroidManifest.xml
:
<uses-permission android:name="android.permission.INTERNET" />
L'application a commencé avec un demi-coup de pied et j'ai obtenu ceci
Au début, j'ai dû bricoler avec le web. Je ne savais pas comment créer un projet Web, j'ai donc utilisé les instructions d'Internet, qui pour une raison quelconque ne fonctionnaient pas. Je voulais déjà cracher, mais je suis tombé sur
ce manuel.
En conséquence, tout s'est avéré être aussi simple que possible - il a seulement fallu inclure la prise en charge des applications Web. Extrait du manuel:
flutter channel master flutter upgrade flutter config --enable-web cd <into project directory> flutter create . flutter run -d chrome
Ensuite, j'ai transféré les fichiers nécessaires à ce projet de la même manière barbare et reçu ces
Impressions générales
Au début, travailler avec Flutter était inhabituel, j'ai constamment essayé d'utiliser les approches habituelles de React Native, et cela a interféré. De plus, une certaine redondance du code de fléchettes était un peu ennuyeuse.
Après avoir pris ma main (et mes cônes) un peu, j'ai commencé à voir les avantages de Flutter par rapport à React Native. J'en énumérerai quelques-uns.
Langue . Dart est un langage complètement compréhensible et agréable avec un typage statique fort. Après JavaScript, c'était comme une bouffée d'air frais. J'ai cessé d'avoir peur que mon code se casse pendant l'exécution et c'était une sensation agréable. Quelqu'un peut dire qu'il y a Flow et TypeScript, mais ce n'est pas cela - dans nos projets, nous avons utilisé les deux, et quelque chose d'autre a toujours éclaté quelque part. Lorsque j'écris dans React Native, je ne peux pas m'empêcher de penser que mon code est sur des accessoires d'allumettes qui peuvent se casser à tout moment. Avec Flutter, j'ai oublié ce sentiment, et si le prix est la redondance du code, alors je suis prêt à le payer.
Plateforme . Dans React Native, vous utilisez des composants natifs, ce qui est généralement bon. Mais à cause de cela, vous devez parfois écrire du code spécifique à la plate-forme, ainsi que des bugs spécifiques à chaque plate-forme. Cela peut être incroyablement fatigant. Avec Flutter, vous pouvez oublier ces problèmes comme un cauchemar (même s'il se peut que dans les grandes applications, les choses ne soient pas si fluides).
L'environnement . Avec l'environnement dans React Native, tout est triste. Les plugins vscode tombent constamment, le débogueur peut engloutir 16 gig de fonctionnement et 70 gig de swap, suspendant étroitement le système (par expérience personnelle), et le scénario de correction d'erreurs le plus courant: "supprimez node_modules, installez à nouveau les packages et essayez de redémarrer plusieurs fois." Cela aide généralement, mais bljad! Il ne devrait pas en être ainsi, non.
De plus, vous devrez exécuter AndroidStudio et Xcode périodiquement, car certains ne sont définis que de cette façon (en toute honnêteté, avec la sortie de RN 0.60, cela s'est amélioré).
Dans ce contexte, le plugin Flutter officiel pour vscode semble très bon. Les astuces pour le code vous permettent de vous familiariser avec la plate-forme sans consulter la documentation, le formatage automatique résout le problème avec le style de codage, le débogueur normal, etc.
En général, cela ressemble à un outil plus mature.
Multiplateforme . React Native professe le principe «Apprenez une fois, écrivez partout» - une fois que vous apprenez, vous pouvez écrire pour différentes plates-formes. Certes, sous chaque plate-forme, vous rencontrerez des problèmes qui lui sont spécifiques. Mais ce n'est peut-être qu'une conséquence de l'immaturité de React Native - pour le moment, la dernière version stable est la 0.61. Peut-être qu'avec la sortie de la version 1.0, la plupart de ces problèmes disparaîtront.
L'approche Flutter ressemble plus à Écrire une fois, compiler partout. Et même si le bureau n'est pas prêt pour la production pour le moment, le web est également en alpha, mais tout y est. Et la possibilité d'avoir une base de code unique pour toutes les plateformes est un argument fort.
Bien sûr, Flutter n'est pas non plus sans défauts, mais un peu d'expérience dans son utilisation ne me permet pas de les identifier. Donc, si vous voulez une évaluation plus objective, n'hésitez pas à réduire l'effet de nouveauté.
En général, il convient de noter que Flutter a laissé des sentiments principalement positifs, bien qu'il ait de la place pour grandir. Et le prochain projet, je serais plus disposé à commencer dessus, et non sur React Native.
Le code source du projet se trouve sur
GitHub .
PS Je profite de cette occasion pour féliciter tous ceux qui ont participé à la dernière Journée des enseignants.