Catégories
Astuces et Design

Développement Web et de bureau réactif avec Flutter – Smashing Magazine

A propos de l'auteur

Passionné de Flutter et Linux, auteur du livre Programming Flutter with the Pragmatic Bookshelf. Blogs plus, tweets moins.
Plus à propos
Carmin

Flutter a déjà fait sensation sur la scène du développement mobile. Maintenant, il prend également de plus gros appareils. Voici ce que vous devez savoir pour être prêt à assumer la tâche de développer des applications Web et de bureau à l'aide de ce merveilleux cadre multiplateforme.

Ce didacticiel n'est pas une introduction à Flutter lui-même. Il existe de nombreux articles, vidéos et plusieurs livres 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 comment 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 cadre Web – en plein écran, se terminant par une note sur le routage basé sur l'URL.

Allons-y!

Qu'est-ce que Flutter, pourquoi c'est important, dans quoi il a évolué, où cela va-t-il

Flutter est le dernier cadre de développement d'applications de Google. Google l'envisage comme universel: 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 sous forme d'applications natives ou de pages Web.

C'est un projet très ambitieux, mais Google a connu un succès incroyable jusqu'à présent, en particulier sous deux aspects: en créant un cadre vraiment 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 -fin framework web 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 à l'heure actuelle.

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 il 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 des archives du SDK.
  • Si Flutter est déjà installé, passez au canal bêta avec $ flutter channel beta, puis effectuez le changement lui-même en mettant à jour votre version Flutter (qui est en fait un git pull sur le dossier d'installation Flutter) avec $ flutter upgrade.

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

$ flutter config --enable-web

La prise en charge des ordinateurs de bureau est beaucoup plus expérimentale, en particulier en raison d'un manque d'outils pour Linux et Windows, ce qui rend le développement de plugins particulièrement difficile, et du fait que les API utilisées sont destinées à une utilisation avec validation de principe et non à production. Ceci est différent du développement Web, qui utilise le compilateur dart2js éprouvé pour les versions, qui ne sont même pas prises 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 basculer vers le master libérer le canal en suivant les mêmes étapes décrites précédemment pour le beta canal. Exécutez ensuite ce qui suit en remplaçant soit linux, windows, ou macos:

$ flutter config --enable--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:

  • Courir flutter doctor pour vérifier les problèmes. Un effet secondaire de cette commande Flutter est qu'elle doit télécharger tous les outils dont elle n'a pas besoin.
  • Courir flutter upgrade.
  • Éteignez et rallumez. L'ancienne réponse de support technique de niveau 1 du redémarrage de 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

La prise en charge de Flutter sur le Web n'est pas mauvaise du tout, et cela se reflète dans la facilité de développement du 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 apparaître une entrée pour Flutter. Fonctionnement flutter run sur un compatible Projet Flutter (plus à ce sujet plus tard) lorsque le seul «périphérique connecté» apparaissant est le serveur Web, Flutter démarrera un serveur Web sur localhost:, qui vous permettra d'accéder à votre application Web Flutter à partir de n'importe quel navigateur.

Si vous avez installé Chrome mais qu'il n'apparaît pas, vous devez définir le CHROME_EXECUTABLE variable d'environnement vers le chemin d'accès au fichier exécutable 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 , remplacement avec la même valeur que vous avez utilisée lors de l'activation du support de bureau. Vous pouvez également créer des binaires dans le build répertoire avec flutter build .

Avant de pouvoir faire tout cela, cependant, vous devez avoir un répertoire contenant ce que Flutter a besoin de construire pour votre plate-forme. Il 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 Flutter.

Quand une application est-elle compatible?

Qu'est-ce que j'ai toujours voulu dire lorsque je mentionne 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 parfaitement clair pour tout le monde et pour éviter tout malentendu, veuillez noter Plugin Flutter est un particulier Forfait Flutter 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 système développé par Google url_launcher package autant que vous le souhaitez (et vous voudrez peut-être, é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, incidemment, n'est d'aucune utilité pour une application Web, donc ne pas pouvoir l'utiliser n'est pas vraiment une erreur, sauf pour le fait que vous devez changer votre code pour le faire fonctionner sur le Web si vous l'utilisez.

Par exemple, vous pouvez utiliser le package shared_preferences, qui repose sur HTML localStorage sur le Web.

Des mises en garde similaires sont valables en ce qui concerne 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, il reste beaucoup plus de travail à faire sur le 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 présente 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 différentes tailles et proportions, sans avoir à exécuter séparément émulateurs d'appareils mobiles.

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

Il y a deux perspectives à partir desquelles cette analyse est effectuée:

  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 l'utiliser pour écrire du code d'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 MediaQueryData du MediaQuery racine 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, comme n'importe quel autre InheritedWidget, avec MediaQuery.of(context), qui a un size propriété, qui est de type Size, et qui a donc deux width et height propriétés du type double.
  2. L'autre façon consiste à utiliser un LayoutBuilder, qui est un widget de générateur (tout comme un StreamBuilder ou un FutureBuilder) qui passe au builder fonction (avec le context) une BoxConstraints objet qui a minHeight, maxHeight, minWidth et maxWidth Propriétés.

Voici un exemple de DartPad utilisant le 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 voici celui qui utilise 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
              )
            )
         )
       )
     )
  );
}

Voyons maintenant quels widgets peuvent s'adapter aux contraintes.

Tout d'abord, réfléchissons aux différentes façons 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 en utilisant le GridView.extent le constructeur n'a même pas besoin de votre implication pour être 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()
      )
   );
}

Vous pouvez accepter du contenu de différentes tailles en modifiant maxCrossAxisExtent.

Cet exemple servait principalement à montrer l’existence du GridView.extent GridView constructeur, 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 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 d'adaptation de GridView à 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 le Cards sont un peu plus complexes et plus grands.

Un changement très simple qui pourrait être apporté aux applications conçues pour les téléphones serait de remplacer un Drawer avec un menu permanent à gauche quand il y a de l'espace.

Par exemple, nous pourrions avoir un ListView de widgets, comme le suivant, qui est utilisé 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 endroit commun à utiliser qui serait à l'intérieur d'un Drawer (également connu sous le nom de menu hamburger).

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

Pour afficher uniquement Drawer contenant le Menu que nous avons vu plus tôt sur des écrans plus petits, vous écririez du code qui ressemble à l'extrait de code suivant, en vérifiant la largeur à l'aide du MediaQuery.of(context) et en passant un Drawer s'opposer à la Scaffold seulement si elle est inférieure à une valeur de largeur que nous pensons appropriée pour notre application:

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

Maintenant, réfléchissons à la body du 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))
        )
      )
    )
  );
}

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

Sur des écrans plus petits, l'ensemble body serait le Content.

Nous allons tout emballer dans un SafeArea et un Center widget parce que parfois les widgets de l'application Web Flutter, en particulier lors de l'utilisation Rowle sable Columns, se retrouvent en dehors de la zone d'écran visible, et qui est fixé avec SafeArea et / ou Center.

Cela signifie que body du Scaffold sera la suivante:

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 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 éléments 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, voyons comment nous pouvons transformer un exemple plus complet en une application réactive, en pensant aux éléments d'application courants et aux flux d'interface utilisateur.

Mettre en contexte: rendre une application adaptée

Jusqu'à présent, nous avons juste un écran. Développons cela en une application à deux écrans avec une navigation basée sur les URL qui fonctionne!

Création d'une page de connexion réactive

Les chances sont que votre application dispose d'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. L'espace disponible n'est pas beaucoup; c'est généralement juste un Column avec une certaine Padding autour de ses widgets, et il contient TextFields 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, un TextEditingController pour chaque TextField) La page de connexion d'une application mobile peut être la suivante:

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 semble bien sur un appareil mobile, mais ceux très larges TextFields commence à avoir l'air secoué 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 une certaine flexibilité.

Par exemple, grâce à l'expérimentation, nous pourrions trouver que la largeur maximale doit être de 500. Eh bien, nous définirions la Container’S constraints à 500 (j'ai utilisé un Container au lieu de Padding dans l'exemple précédent parce que je savais où j'allais avec ça) et nous sommes prêts à aller, non? 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 dans un Center widget, 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à très bien, et nous n'avons même pas eu à utiliser soit un LayoutBuilder ou la MediaQuery.of(context).size. Allons un peu plus loin pour que ce soit très beau, cependant. À mon avis, ce serait mieux 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 de fond à ce qui se cache derrière le Container avec les widgets d'entrée, et en gardant le premier plan Container blanc. Pour le rendre un peu meilleur, gardons le Container de l'étirement vers le haut et le bas de l'écran sur les gros appareils, donnez-lui des coins arrondis et donnez-lui une belle transition animée entre les deux dispositions.

Tout cela nécessite maintenant un LayoutBuilder et un extérieur Container afin à la fois de définir une couleur d'arrière-plan et d'ajouter un rembourrage tout autour du Container et pas seulement sur les côtés uniquement sur des écrans plus grands. De plus, pour que le changement de la quantité de rembourrage soit animé, il suffit de tourner Container dans un AnimatedContainer, ce qui nécessite un duration pour l'animation, que nous allons définir à une demi-seconde, ce qui est Duration(milliseconds: 500) dans du 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é RaisedButton’S onPressed à 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 application Web appropriée

Une chose courante pour les applications Web est la possibilité de changer les écrans en fonction de l'URL. Par exemple, aller à https://appurl/login devrait vous donner quelque chose de différent de https://appurl/somethingelse. Flutter, en fait, soutient 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 changer le MaterialApp constructeur à celui qui ressemble à ceci:

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

Et puis nous pouvons passer à un itinéraire différent 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 créée 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 cela sur votre propre machine avec flutter runou consultez 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 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 route 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 sa prise en charge 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

Smashing Editorial(ra, yk, il, al)

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *