Développement Web et Desktop réactif avec Flutter

Publié: 2022-03-10
Résumé rapide ↬ Flutter a déjà fait sensation sur la scène du développement mobile. Maintenant, il prend également en charge des appareils plus gros. Voici ce que vous devez savoir pour être prêt à développer des applications Web et de bureau à l'aide de ce merveilleux framework multiplateforme.

Ce tutoriel n'est pas une introduction à Flutter lui-même. De nombreux articles, vidéos et plusieurs livres sont disponibles en ligne avec des introductions simples qui vous aideront à apprendre les bases de Flutter. Au lieu de cela, nous couvrirons les deux objectifs suivants :

  1. L'état actuel du développement non mobile de Flutter et la façon dont vous pouvez exécuter le code Flutter dans le navigateur, sur un ordinateur de bureau ou portable ;
  2. Comment créer des applications réactives à l'aide de Flutter, afin que vous puissiez voir sa puissance - en particulier en tant que framework Web - en plein affichage, se terminant par une note sur le routage basé sur l'URL.

Allons-y !

Qu'est-ce que le flutter, pourquoi est-il important, en quoi a-t-il évolué, où va-t-il ?

Flutter est le dernier cadre de développement d'applications de Google. Google l'envisage comme global : il permettra d'exécuter le même code sur les smartphones de toutes les marques, sur les tablettes et sur les ordinateurs de bureau et portables en tant qu'applications natives ou en tant que pages Web.

C'est un projet très ambitieux, mais Google a connu un succès incroyable jusqu'à présent, en particulier dans deux aspects : en créant un cadre véritablement indépendant de la plate-forme pour les applications natives Android et iOS qui fonctionne très bien et est entièrement prêt pour une utilisation en production, et en créant un front impressionnant -framework web final qui peut partager 100% du code avec une application Flutter compatible.

Dans la section suivante, nous allons voir ce qui rend l'application compatible et quel est l'état du développement Flutter non mobile à ce jour.

Plus après saut! Continuez à lire ci-dessous ↓

Développement non mobile avec Flutter

Le développement non mobile avec Flutter a été annoncé pour la première fois de manière significative lors de Google I/O 2019. Cette section explique comment le faire fonctionner et quand cela fonctionne.

Comment activer le développement Web et de bureau

Pour activer le développement Web, vous devez d'abord être sur le canal bêta de Flutter. Il y a deux façons d'arriver à ce point :

  • Installez Flutter directement sur le canal bêta en téléchargeant la dernière version bêta appropriée à partir de l'archive SDK.
  • Si vous avez déjà installé Flutter, passez au canal bêta avec $ flutter channel beta , puis effectuez le changement lui-même en mettant à jour votre version de Flutter (qui est en fait un git pull sur le dossier d'installation de Flutter) avec $ flutter upgrade .

Après cela, vous pouvez exécuter ceci :

 $ flutter config --enable-web

Le support de bureau est beaucoup plus expérimental, notamment en raison d'un manque d'outils pour Linux et Windows, ce qui rend le développement de plugins particulièrement pénible, et en raison du fait que les API utilisées pour cela sont destinées à une utilisation de preuve de concept et non à production. Ceci est différent du développement Web, qui utilise le compilateur dart2js éprouvé pour les versions de version, qui ne sont même pas pris en charge pour les applications de bureau natives Windows et Linux.

Remarque : la prise en charge de macOS est légèrement meilleure que la prise en charge de Windows et Linux, mais elle n'est toujours pas aussi bonne que la prise en charge du Web et pas aussi bonne que la prise en charge complète des plates-formes mobiles.

Pour activer la prise en charge du développement de bureau, vous devez passer au canal de version master en suivant les mêmes étapes décrites précédemment pour le canal beta . Ensuite, exécutez ce qui suit en remplaçant <OS_NAME> par linux , windows ou macos :

 $ flutter config --enable-<OS_NAME>-desktop

À ce stade, si vous rencontrez des problèmes avec l'une des étapes suivantes que je vais décrire parce que l'outil Flutter ne fait pas ce que je dis qu'il devrait faire, voici quelques étapes de dépannage courantes :

  • Exécutez flutter doctor pour vérifier les problèmes. Un effet secondaire de cette commande Flutter est qu'elle devrait télécharger tous les outils dont elle a besoin et qu'elle n'a pas.
  • Exécutez flutter upgrade .
  • Éteignez-le et rallumez-le. L'ancienne réponse du support technique de niveau 1 consistant à redémarrer votre ordinateur pourrait être exactement ce dont vous avez besoin pour pouvoir profiter de toutes les richesses de Flutter.

Exécution et création d'applications Web Flutter

Le support Web Flutter n'est pas mal du tout, et cela se reflète dans la facilité de développement pour le Web.

Exécuter ceci…

 $ flutter devices

… devrait afficher immédiatement une entrée pour quelque chose comme ceci :

 Web Server • web-server • web-javascript • Flutter Tools

De plus, l'exécution du navigateur Chrome devrait également faire en sorte que Flutter affiche une entrée pour celui-ci. L' flutter run de Flutter sur un projet Flutter compatible (plus à ce sujet plus tard) lorsque le seul "appareil connecté" affiché est le serveur Web, Flutter démarrera un serveur Web sur localhost:<RANDOM_PORT> , ce qui vous permettra d'accéder à votre Flutter application Web depuis n'importe quel navigateur.

Si vous avez installé Chrome mais qu'il ne s'affiche pas, vous devez définir la variable d'environnement CHROME_EXECUTABLE sur le chemin d'accès au fichier exécutable de Chrome.

Exécution et création d'applications de bureau Flutter

Après avoir activé la prise en charge du bureau Flutter, vous pouvez exécuter une application Flutter en mode natif sur votre poste de travail de développement avec flutter run -d <OS_NAME> , en remplaçant <OS_NAME> par la même valeur que vous avez utilisée lors de l'activation de la prise en charge du bureau. Vous pouvez également créer des fichiers binaires dans le répertoire de build avec flutter build <OS_NAME> .

Cependant, avant de pouvoir faire quoi que ce soit, vous devez disposer d'un répertoire contenant ce que Flutter doit créer pour votre plate-forme. Celui-ci sera créé automatiquement lorsque vous créerez un nouveau projet, mais vous devrez le créer pour un projet existant avec flutter create . . De plus, les API Linux et Windows sont instables, vous devrez donc peut-être les régénérer pour ces plates-formes si l'application cesse de fonctionner après une mise à jour de Flutter.

Quand une application est-elle compatible ?

Qu'est-ce que j'ai toujours voulu dire en mentionnant qu'une application Flutter doit être un "projet compatible" pour qu'elle fonctionne sur le bureau ou sur le Web ? En termes simples, je veux dire qu'il ne doit pas utiliser de plugin qui n'a pas d'implémentation spécifique à la plate-forme pour la plate-forme sur laquelle vous essayez de construire.

Pour que ce point soit absolument clair pour tout le monde et éviter tout malentendu, veuillez noter qu'un plugin Flutter est un package Flutter particulier qui contient le code spécifique à la plate-forme qui lui est nécessaire pour fournir ses fonctionnalités.

Par exemple, vous pouvez utiliser le package url_launcher développé par Google autant que vous le souhaitez (et vous voudrez peut-être le faire, étant donné que le Web est construit sur des hyperliens).

Un exemple de package développé par Google dont l'utilisation empêcherait le développement Web est path_provider , qui est utilisé pour obtenir le chemin de stockage local dans lequel enregistrer les fichiers. Ceci est un exemple de package qui, soit dit en passant, n'est d'aucune utilité pour une application Web, donc ne pas pouvoir l'utiliser n'est pas vraiment une déception, à l'exception du fait que vous devez modifier votre code pour pour qu'il fonctionne sur le Web si vous l'utilisez.

Par exemple, vous pouvez utiliser le package shared_preferences, qui s'appuie sur HTML localStorage sur le Web.

Des mises en garde similaires sont valables concernant les plates-formes de bureau : très peu de plug-ins sont compatibles avec les plates-formes de bureau et, comme il s'agit d'un thème récurrent, beaucoup plus de travail à ce sujet doit être fait du côté du bureau que ce qui est vraiment nécessaire sur Flutter pour le Web.

Création de mises en page réactives dans Flutter

En raison de ce que j'ai décrit ci-dessus et pour des raisons de simplicité, je vais supposer pour le reste de cet article que votre plate-forme cible est le Web, mais les concepts de base s'appliquent également au développement de bureau.

Soutenir le Web a des avantages et des responsabilités. Être à peu près obligé de prendre en charge différentes tailles d'écran peut sembler un inconvénient, mais considérez que l'exécution de l'application dans les navigateurs Web vous permet de voir très facilement à quoi ressemblera votre application sur des écrans de tailles et de rapports d'aspect différents, sans avoir à exécuter séparément émulateurs d'appareils mobiles.

Maintenant, parlons code. Comment pouvez-vous rendre votre application réactive ?

Il y a deux perspectives à partir desquelles cette analyse est faite :

  1. « Quels widgets est-ce que j'utilise ou puis-je utiliser qui peuvent ou doivent s'adapter à des écrans de différentes tailles ? »
  2. "Comment puis-je obtenir des informations sur la taille de l'écran et comment puis-je l'utiliser lors de l'écriture du code de l'interface utilisateur ?"

Nous répondrons à la première question plus tard. Parlons d'abord de ce dernier, car il peut être traité très facilement et est au cœur du problème. Il y a deux façons de faire ça:

  1. Une façon consiste à prendre les informations de MediaQueryData de la racine MediaQuery InheritedWidget , qui doit exister dans l'arborescence des widgets pour qu'une application Flutter fonctionne (elle fait partie de MaterialApp/WidgetsApp/CupertinoApp ), que vous pouvez obtenir, tout comme tout autre InheritedWidget , avec MediaQuery.of(context) , qui a une propriété size , qui est de type Size , et qui a donc deux propriétés width et height de type double .
  2. L'autre méthode consiste à utiliser un LayoutBuilder , qui est un widget de construction (tout comme un StreamBuilder ou un FutureBuilder ) qui transmet à la fonction de builder (avec le context ) un objet BoxConstraints qui a minHeight , maxHeight , minWidth et maxWidth propriétés.

Voici un exemple DartPad utilisant MediaQuery pour obtenir des contraintes, dont le code est le suivant :

 import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(context) => MaterialApp( home: MyHomePage() ); } class MyHomePage extends StatelessWidget { @override Widget build(context) => Scaffold( body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Text( "Width: ${MediaQuery.of(context).size.width}", style: Theme.of(context).textTheme.headline4 ), Text( "Height: ${MediaQuery.of(context).size.height}", style: Theme.of(context).textTheme.headline4 ) ] ) ) ); }

Et en voici un utilisant le LayoutBuilder pour la même chose :

 import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(context) => MaterialApp( home: MyHomePage() ); } class MyHomePage extends StatelessWidget { @override Widget build(context) => Scaffold( body: LayoutBuilder( builder: (context, constraints) => Center( child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Text( "Width: ${constraints.maxWidth}", style: Theme.of(context).textTheme.headline4 ), Text( "Height: ${constraints.maxHeight}", style: Theme.of(context).textTheme.headline4 ) ] ) ) ) ); }

Maintenant, réfléchissons aux widgets qui peuvent s'adapter aux contraintes.

Tout d'abord, réfléchissons aux différentes manières de disposer plusieurs widgets en fonction de la taille de l'écran.

Le widget qui s'adapte le plus facilement est le GridView . En fait, un GridView construit à l'aide du constructeur GridView.extent n'a même pas besoin de votre implication pour être rendu réactif, comme vous pouvez le voir dans cet exemple très simple :

 import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(context) => MaterialApp( home: MyHomePage() ); } class MyHomePage extends StatelessWidget { final List elements = [ "Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "A Million Billion Trillion", "A much, much longer text that will still fit" ]; @override Widget build(context) => Scaffold( body: GridView.extent( maxCrossAxisExtent: 130.0, crossAxisSpacing: 20.0, mainAxisSpacing: 20.0, children: elements.map((el) => Card(child: Center(child: Padding(padding: EdgeInsets.all(8.0), child: Text(el))))).toList() ) ); } import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(context) => MaterialApp( home: MyHomePage() ); } class MyHomePage extends StatelessWidget { final List elements = [ "Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "A Million Billion Trillion", "A much, much longer text that will still fit" ]; @override Widget build(context) => Scaffold( body: GridView.extent( maxCrossAxisExtent: 130.0, crossAxisSpacing: 20.0, mainAxisSpacing: 20.0, children: elements.map((el) => Card(child: Center(child: Padding(padding: EdgeInsets.all(8.0), child: Text(el))))).toList() ) ); }

Vous pouvez adapter le contenu de différentes tailles en modifiant le maxCrossAxisExtent .

Cet exemple a principalement servi à montrer l'existence du constructeur GridView.extent GridView , mais une façon beaucoup plus intelligente de le faire serait d'utiliser un GridView.builder avec un SliverGridDelegateWithMaxCrossAxisExtent , dans ce cas où les widgets à afficher dans la grille sont créés dynamiquement à partir d'une autre structure de données, comme vous pouvez le voir dans cet exemple :

 import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(context) => MaterialApp( home: MyHomePage() ); } class MyHomePage extends StatelessWidget { final List<String> elements = ["Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "A Million Billion Trillion", "A much, much longer text that will still fit"]; @override Widget build(context) => Scaffold( body: GridView.builder( itemCount: elements.length, gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 130.0, crossAxisSpacing: 20.0, mainAxisSpacing: 20.0, ), itemBuilder: (context, i) => Card( child: Center( child: Padding( padding: EdgeInsets.all(8.0), child: Text(elements[i]) ) ) ) ) ); }

Un exemple de GridView s'adaptant à différents écrans est ma page de destination personnelle, qui est une application Web Flutter très simple composée d'un GridView avec un tas de Cards , tout comme cet exemple de code précédent, sauf que les Cards sont un peu plus complexe et plus grand .

Une modification très simple qui pourrait être apportée aux applications conçues pour les téléphones serait de remplacer un Drawer par un menu permanent à gauche lorsqu'il y a de la place.

Par exemple, nous pourrions avoir une ListView de widgets, comme celle-ci, qui est utilisée pour la navigation :

 class Menu extends StatelessWidget { @override Widget build(context) => ListView( children: [ FlatButton( onPressed: () {}, child: ListTile( leading: Icon(Icons.looks_one), title: Text("First Link"), ) ), FlatButton( onPressed: () {}, child: ListTile( leading: Icon(Icons.looks_two), title: Text("Second Link"), ) ) ] ); }

Sur un smartphone, un lieu commun à utiliser qui serait à l'intérieur d'un Drawer (également appelé menu hamburger).

Des alternatives à cela seraient BottomNavigationBar ou TabBar , en combinaison avec TabBarView , mais avec les deux, nous devrions apporter plus de modifications que nécessaire avec le tiroir, nous nous en tiendrons donc au tiroir.

Pour afficher uniquement le Drawer contenant le Menu que nous avons vu précédemment sur des écrans plus petits, vous devez écrire du code qui ressemble à l'extrait de code suivant, en vérifiant la largeur à l'aide de MediaQuery.of(context) et en transmettant un objet Drawer à l' Scaffold uniquement s'il est inférieure à une valeur de largeur que nous pensons être appropriée pour notre application :

 Scaffold( appBar: AppBar(/* ... \*/), drawer: MediaQuery.of(context).size.width < 500 ? Drawer( child: Menu(), ) : null, body: /* ... \*/ )

Maintenant, pensons au body de l' Scaffold . Comme exemple de contenu principal de notre application, nous utiliserons le GridView que nous avons construit précédemment, que nous conservons dans un widget séparé nommé Content pour éviter toute confusion :

 class Content extends StatelessWidget { final List elements = ["Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "A Million Billion Trillion", "A much, much longer text that will still fit"]; @override Widget build(context) => GridView.builder( itemCount: elements.length, gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 130.0, crossAxisSpacing: 20.0, mainAxisSpacing: 20.0, ), itemBuilder: (context, i) => Card( child: Center( child: Padding( padding: EdgeInsets.all(8.0), child: Text(elements[i]) ) ) ) ); } class Content extends StatelessWidget { final List elements = ["Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "A Million Billion Trillion", "A much, much longer text that will still fit"]; @override Widget build(context) => GridView.builder( itemCount: elements.length, gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 130.0, crossAxisSpacing: 20.0, mainAxisSpacing: 20.0, ), itemBuilder: (context, i) => Card( child: Center( child: Padding( padding: EdgeInsets.all(8.0), child: Text(elements[i]) ) ) ) ); }

Sur les écrans plus grands, le corps lui-même peut être une Row qui affiche deux widgets : le Menu , qui est limité à une largeur fixe, et le Content remplissant le reste de l'écran.

Sur des écrans plus petits, le body entier serait le Content .

Nous allons tout encapsuler dans un SafeArea et un widget Center car parfois les widgets d'applications Web Flutter, en particulier lors de l'utilisation de Row s et Column s, se retrouvent en dehors de la zone d'écran visible, et cela est corrigé avec SafeArea et/ou Center .

Cela signifie que le body de l' Scaffold sera le suivant :

 SafeArea( child:Center( child: MediaQuery.of(context).size.width < 500 ? Content() : Row( children: [ Container( width: 200.0, child: Menu() ), Container( width: MediaQuery.of(context).size.width-200.0, child: Content() ) ] ) ) )

Voici tout cela réuni :

( Grand aperçu )
 import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(context) => MaterialApp( home: HomePage() ); } class HomePage extends StatelessWidget { @override Widget build(context) => Scaffold( appBar: AppBar(title: Text("test")), drawer: MediaQuery.of(context).size.width < 500 ? Drawer( child: Menu(), ) : null, body: SafeArea( child:Center( child: MediaQuery.of(context).size.width < 500 ? Content() : Row( children: [ Container( width: 200.0, child: Menu() ), Container( width: MediaQuery.of(context).size.width-200.0, child: Content() ) ] ) ) ) ); } class Menu extends StatelessWidget { @override Widget build(context) => ListView( children: [ FlatButton( onPressed: () {}, child: ListTile( leading: Icon(Icons.looks_one), title: Text("First Link"), ) ), FlatButton( onPressed: () {}, child: ListTile( leading: Icon(Icons.looks_two), title: Text("Second Link"), ) ) ] ); } class Content extends StatelessWidget { final List<String> elements = ["Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "A Million Billion Trillion", "A much, much longer text that will still fit"]; @override Widget build(context) => GridView.builder( itemCount: elements.length, gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 130.0, crossAxisSpacing: 20.0, mainAxisSpacing: 20.0, ), itemBuilder: (context, i) => Card( child: Center( child: Padding( padding: EdgeInsets.all(8.0), child: Text(elements[i]) ) ) ) ); }

C'est la plupart des choses dont vous aurez besoin comme introduction générale à l'interface utilisateur réactive dans Flutter. Une grande partie de son application dépendra de l'interface utilisateur spécifique de votre application, et il est difficile de déterminer exactement ce que vous pouvez faire pour rendre votre application réactive, et vous pouvez adopter de nombreuses approches en fonction de vos préférences. Maintenant, cependant, voyons comment nous pouvons transformer un exemple plus complet en une application réactive, en pensant aux éléments d'application communs et aux flux d'interface utilisateur.

Mise en contexte : rendre une application réactive

Jusqu'à présent, nous n'avons qu'un écran. Développons cela dans une application à deux écrans avec une navigation basée sur une URL fonctionnelle !

Créer une page de connexion réactive

Il y a de fortes chances que votre application ait une page de connexion. Comment pouvons-nous rendre cela réactif?

Les écrans de connexion sur les appareils mobiles sont généralement assez similaires les uns aux autres. L'espace disponible n'est pas beaucoup; c'est généralement juste une Column avec du Padding autour de ses widgets, et elle contient des TextField pour taper un nom d'utilisateur et un mot de passe et un bouton pour se connecter. Donc, un joli standard (bien que ne fonctionnant pas, car cela nécessiterait, entre autres choses , un TextEditingController pour chaque TextField ) page de connexion pour une application mobile pourrait être le suivant :

 Scaffold( body: Container( padding: const EdgeInsets.symmetric( vertical: 30.0, horizontal: 25.0 ), child: Column( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Text("Welcome to the app, please log in"), TextField( decoration: InputDecoration( labelText: "username" ) ), TextField( obscureText: true, decoration: InputDecoration( labelText: "password" ) ), RaisedButton( color: Colors.blue, child: Text("Log in", style: TextStyle(color: Colors.white)), onPressed: () {} ) ] ), ), )

Cela a l'air bien sur un appareil mobile, mais ces champs de TextField très larges commencent à avoir l'air discordants sur une tablette, sans parler d'un écran plus grand. Cependant, nous ne pouvons pas simplement décider d'une largeur fixe car les téléphones ont des tailles d'écran différentes, et nous devons maintenir un certain degré de flexibilité.

Par exemple, grâce à l'expérimentation, nous pourrions constater que la largeur maximale devrait être de 500. Eh bien, nous définirions les constraints du Container sur 500 (j'ai utilisé un Container au lieu de Padding dans l'exemple précédent car je savais où j'allais avec ce ) et nous sommes prêts à partir, n'est-ce pas ? Pas vraiment, car cela ferait coller les widgets de connexion sur le côté gauche de l'écran, ce qui pourrait être encore pire que de tout étirer. Donc, nous enveloppons un widget Center , comme ceci :

 Center( child: Container( constraints: BoxConstraints(maxWidth: 500), padding: const EdgeInsets.symmetric( vertical: 30.0, horizontal: 25.0 ), child: Column(/* ... \*/) ) )

Cela semble déjà bien, et nous n'avons même pas eu à utiliser un LayoutBuilder ou le MediaQuery.of(context).size . Allons un peu plus loin pour que cela ait l'air très bien, cependant. Ce serait mieux, à mon avis, si la partie de premier plan était en quelque sorte séparée de l'arrière-plan. Nous pouvons y parvenir en donnant une couleur d'arrière-plan à ce qui se trouve derrière le Container avec les widgets d'entrée et en gardant le Container de premier plan blanc. Pour le rendre un peu meilleur, empêchons le Container de s'étirer vers le haut et le bas de l'écran sur les grands appareils, donnons-lui des coins arrondis et donnons-lui une belle transition animée entre les deux dispositions.

Tout cela nécessite désormais un LayoutBuilder et un Container externe afin à la fois de définir une couleur d'arrière-plan et d'ajouter du rembourrage tout autour du Container et pas seulement sur les côtés uniquement sur les écrans plus grands. De plus, pour rendre le changement de quantité de rembourrage animé, nous avons juste besoin de transformer ce Container externe en un AnimatedContainer , ce qui nécessite une duration pour l'animation, que nous allons définir sur une demi-seconde, qui est Duration(milliseconds: 500) dans code.

Voici cet exemple de page de connexion réactive :

( Grand aperçu )
 class LoginPage extends StatelessWidget { @override Widget build(context) => Scaffold( body: LayoutBuilder( builder: (context, constraints) { return AnimatedContainer( duration: Duration(milliseconds: 500), color: Colors.lightGreen[200], padding: constraints.maxWidth < 500 ? EdgeInsets.zero : EdgeInsets.all(30.0), child: Center( child: Container( padding: EdgeInsets.symmetric( vertical: 30.0, horizontal: 25.0 ), constraints: BoxConstraints( maxWidth: 500, ), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(5.0), ), child: Column( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Text("Welcome to the app, please log in"), TextField( decoration: InputDecoration( labelText: "username" ) ), TextField( obscureText: true, decoration: InputDecoration( labelText: "password" ) ), RaisedButton( color: Colors.blue, child: Text("Log in", style: TextStyle(color: Colors.white)), onPressed: () { Navigator.pushReplacement( context, MaterialPageRoute( builder: (context) => HomePage() ) ); } ) ] ), ), ) ); } ) ); }

Comme vous pouvez le voir, j'ai également changé le RaisedButton de onPressed en un rappel qui nous dirige vers un écran nommé HomePage (qui pourrait être, par exemple, la vue que nous avons construite précédemment avec un GridView et un menu ou un tiroir). Maintenant, cependant, c'est sur cette partie de la navigation que nous allons nous concentrer.

Itinéraires nommés : rendre la navigation de votre application plus semblable à une véritable application Web

Une chose courante pour les applications Web est la possibilité de changer d'écran en fonction de l'URL. Par exemple, aller sur https://appurl/login devrait vous donner quelque chose de différent de https://appurl/somethingelse . Flutter, en fait, prend en charge les routes nommées , qui ont deux objectifs :

  1. Dans une application Web, ils ont exactement cette fonctionnalité que j'ai mentionnée dans la phrase précédente.
  2. Dans n'importe quelle application, ils vous permettent de prédéfinir des itinéraires pour votre application et de leur donner des noms, puis de pouvoir y accéder simplement en spécifiant leur nom.

Pour ce faire, nous devons remplacer le constructeur MaterialApp par un constructeur qui ressemble à ceci :

 MaterialApp( initialRoute: "/login", routes: { "/login": (context) => LoginPage(), "/home": (context) => HomePage() } );

Et puis nous pouvons passer à une route différente en utilisant Navigator.pushNamed(context, routeName) et Navigator.pushReplacementNamed(context, routeName) , au lieu de Navigator.push(context, route) et Navigator.pushReplacement(context, route) .

Voici celle appliquée à l'application hypothétique que nous avons construite dans le reste de cet article. Vous ne pouvez pas vraiment voir les itinéraires nommés en action dans DartPad, vous devriez donc essayer ceci sur votre propre machine avec flutter run ou vérifier l'exemple en action :

( Grand aperçu )
 import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(context) => MaterialApp( initialRoute: "/login", routes: { "/login": (context) => LoginPage(), "/home": (context) => HomePage() } ); } class LoginPage extends StatelessWidget { @override Widget build(context) => Scaffold( body: LayoutBuilder( builder: (context, constraints) { return AnimatedContainer( duration: Duration(milliseconds: 500), color: Colors.lightGreen[200], padding: constraints.maxWidth < 500 ? EdgeInsets.zero : const EdgeInsets.all(30.0), child: Center( child: Container( padding: const EdgeInsets.symmetric( vertical: 30.0, horizontal: 25.0 ), constraints: BoxConstraints( maxWidth: 500, ), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(5.0), ), child: Column( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Text("Welcome to the app, please log in"), TextField( decoration: InputDecoration( labelText: "username" ) ), TextField( obscureText: true, decoration: InputDecoration( labelText: "password" ) ), RaisedButton( color: Colors.blue, child: Text("Log in", style: TextStyle(color: Colors.white)), onPressed: () { Navigator.pushReplacementNamed( context, "/home" ); } ) ] ), ), ) ); } ) ); } class HomePage extends StatelessWidget { @override Widget build(context) => Scaffold( appBar: AppBar(title: Text("test")), drawer: MediaQuery.of(context).size.width < 500 ? Drawer( child: Menu(), ) : null, body: SafeArea( child:Center( child: MediaQuery.of(context).size.width < 500 ? Content() : Row( children: [ Container( width: 200.0, child: Menu() ), Container( width: MediaQuery.of(context).size.width-200.0, child: Content() ) ] ) ) ) ); } class Menu extends StatelessWidget { @override Widget build(context) => ListView( children: [ FlatButton( onPressed: () {}, child: ListTile( leading: Icon(Icons.looks_one), title: Text("First Link"), ) ), FlatButton( onPressed: () {}, child: ListTile( leading: Icon(Icons.looks_two), title: Text("Second Link"), ) ), FlatButton( onPressed: () {Navigator.pushReplacementNamed( context, "/login");}, child: ListTile( leading: Icon(Icons.exit_to_app), title: Text("Log Out"), ) ) ] ); } class Content extends StatelessWidget { final List<String> elements = ["Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "A Million Billion Trillion", "A much, much longer text that will still fit"]; @override Widget build(context) => GridView.builder( itemCount: elements.length, gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 130.0, crossAxisSpacing: 20.0, mainAxisSpacing: 20.0, ), itemBuilder: (context, i) => Card( child: Center( child: Padding( padding: EdgeInsets.all(8.0), child: Text(elements[i]) ) ) ) ); }

En avant avec votre aventure Flutter

Cela devrait vous donner une idée de ce que vous pouvez faire avec Flutter sur des écrans plus grands, en particulier sur le Web. C'est un cadre charmant, très facile à utiliser, et son support multiplateforme extrême ne fait que le rendre plus essentiel pour apprendre et commencer à utiliser. Alors, allez-y et faites également confiance à Flutter pour les applications Web !

Autres ressources

  • "Boîtiers de bureau", GitHub
    L'état actuel et toujours à jour de Flutter sur le bureau
  • "Prise en charge du bureau pour Flutter", Flutter
    Informations sur les plates-formes de bureau entièrement prises en charge
  • "Support Web pour Flutter", Flutter
    Informations sur Flutter pour le Web
  • "Tous les échantillons", Flutter
    Une liste organisée d'échantillons et d'applications Flutter