Développement Web et Desktop réactif avec Flutter
Publié: 2022-03-10Ce 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 :
- 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 ;
- 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.
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 ungit 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 :
- « Quels widgets est-ce que j'utilise ou puis-je utiliser qui peuvent ou doivent s'adapter à des écrans de différentes tailles ? »
- "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:
- Une façon consiste à prendre les informations de
MediaQueryData
de la racineMediaQuery
InheritedWidget
, qui doit exister dans l'arborescence des widgets pour qu'une application Flutter fonctionne (elle fait partie deMaterialApp/WidgetsApp/CupertinoApp
), que vous pouvez obtenir, tout comme tout autreInheritedWidget
, avecMediaQuery.of(context)
, qui a une propriétésize
, qui est de typeSize
, et qui a donc deux propriétéswidth
etheight
de typedouble
. - L'autre méthode consiste à utiliser un
LayoutBuilder
, qui est un widget de construction (tout comme unStreamBuilder
ou unFutureBuilder
) qui transmet à la fonction debuilder
(avec lecontext
) un objetBoxConstraints
qui aminHeight
,maxHeight
,minWidth
etmaxWidth
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 :

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 :

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 :
- Dans une application Web, ils ont exactement cette fonctionnalité que j'ai mentionnée dans la phrase précédente.
- 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 :

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