Responsive Web- und Desktop-Entwicklung mit Flutter

Veröffentlicht: 2022-03-10
Kurze Zusammenfassung ↬ Flutter hat in der mobilen Entwicklungsszene bereits für Furore gesorgt. Jetzt nimmt es auch größere Geräte auf. Hier ist, was Sie wissen müssen, um bereit zu sein, die Aufgabe der Entwicklung von Web- und Desktop-Apps mit diesem wunderbaren plattformübergreifenden Framework zu übernehmen.

Dieses Tutorial ist keine Einführung in Flutter selbst. Es gibt viele Artikel, Videos und mehrere Bücher online mit einfachen Einführungen, die Ihnen helfen, die Grundlagen von Flutter zu lernen. Stattdessen behandeln wir die folgenden zwei Ziele:

  1. Der aktuelle Stand der nicht-mobilen Flutter-Entwicklung und wie Sie Flutter-Code im Browser, auf einem Desktop- oder Laptop-Computer ausführen können;
  2. So erstellen Sie mit Flutter reaktionsschnelle Apps, damit Sie ihre Leistungsfähigkeit – insbesondere als Web-Framework – in vollem Umfang sehen können, und enden mit einem Hinweis zum Routing auf der Grundlage von URLs.

Lassen Sie uns darauf eingehen!

Was ist Flutter, warum ist es wichtig, wozu hat es sich entwickelt, wohin es führt

Flutter ist Googles neuestes App-Entwicklungs-Framework. Google stellt es sich allumfassend vor: Es wird ermöglichen, dass derselbe Code auf Smartphones aller Marken, auf Tablets und auf Desktop- und Laptop-Computern als native Apps oder als Webseiten ausgeführt wird.

Es ist ein sehr ehrgeiziges Projekt, aber Google war bisher vor allem in zwei Aspekten unglaublich erfolgreich: bei der Schaffung eines wirklich plattformunabhängigen Frameworks für native Android- und iOS-Apps, das großartig funktioniert und vollständig produktionsreif ist, und bei der Schaffung einer beeindruckenden Fassade -End-Web-Framework, das 100 % des Codes mit einer kompatiblen Flutter-App teilen kann.

Im nächsten Abschnitt werden wir sehen, was die App kompatibel macht und wie der aktuelle Stand der nicht-mobilen Flutter-Entwicklung ist.

Mehr nach dem Sprung! Lesen Sie unten weiter ↓

Nicht-mobile Entwicklung mit Flutter

Die nicht-mobile Entwicklung mit Flutter wurde erstmals auf der Google I/O 2019 in großem Umfang bekannt gemacht. In diesem Abschnitt geht es darum, wie es funktioniert und wann es funktioniert.

So aktivieren Sie die Web- und Desktop-Entwicklung

Um die Webentwicklung zu aktivieren, müssen Sie sich zunächst im Beta-Kanal von Flutter befinden. Es gibt zwei Möglichkeiten, an diesen Punkt zu gelangen:

  • Installieren Sie Flutter direkt im Beta-Kanal, indem Sie die entsprechende neueste Beta-Version aus dem SDK-Archiv herunterladen.
  • Wenn Sie Flutter bereits installiert haben, wechseln Sie mit $ flutter channel beta zum Beta-Kanal und führen Sie dann den Wechsel selbst durch, indem Sie Ihre Flutter-Version (die eigentlich ein git pull im Flutter-Installationsordner ist) mit $ flutter upgrade .

Danach können Sie Folgendes ausführen:

 $ flutter config --enable-web

Die Desktop-Unterstützung ist viel experimenteller, insbesondere aufgrund fehlender Tools für Linux und Windows, was die Plugin-Entwicklung besonders zu einem großen Problem macht, und aufgrund der Tatsache, dass die dafür verwendeten APIs für die Proof-of-Concept-Nutzung und nicht für Produktion. Dies ist anders als bei der Webentwicklung, die den bewährten dart2js-Compiler für Release-Builds verwendet, die nicht einmal für Windows- und Linux-native Desktop-Apps unterstützt werden.

Hinweis : Die Unterstützung für macOS ist etwas besser als die Unterstützung für Windows und Linux, aber sie ist immer noch nicht so gut wie die Unterstützung für das Web und nicht annähernd so gut wie die vollständige Unterstützung für mobile Plattformen.

Um die Unterstützung für die Desktop-Entwicklung zu aktivieren, müssen Sie zum master -Release-Kanal wechseln, indem Sie dieselben Schritte ausführen, die zuvor für den beta -Kanal beschrieben wurden. Führen Sie dann Folgendes aus, indem <OS_NAME> entweder durch linux , windows oder macos ersetzen:

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

Wenn Sie an dieser Stelle Probleme mit einem der folgenden Schritte haben, die ich beschreiben werde, weil das Flutter-Tool nicht das tut, was ich sage, sind einige allgemeine Schritte zur Fehlerbehebung:

  • Führen Sie flutter doctor aus, um nach Problemen zu suchen. Ein Nebeneffekt dieses Flutter-Befehls ist, dass er alle benötigten Tools herunterladen sollte, die er nicht hat.
  • flutter upgrade .
  • Schalten Sie es aus und wieder ein. Die alte Antwort des technischen Supports der Stufe 1, Ihren Computer neu zu starten, könnte genau das sein, was Sie brauchen, um den vollen Reichtum von Flutter genießen zu können.

Ausführen und Erstellen von Flutter-Web-Apps

Die Flutter-Webunterstützung ist überhaupt nicht schlecht, und dies spiegelt sich in der einfachen Entwicklung für das Web wider.

Ausführen dieses…

 $ flutter devices

… sollte gleich einen Eintrag für so etwas zeigen:

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

Außerdem sollte das Ausführen des Chrome-Browsers dazu führen, dass Flutter auch einen Eintrag dafür anzeigt. Das Ausführen von flutter run auf einem kompatiblen Flutter-Projekt (dazu später mehr), wenn das einzige „verbundene Gerät“, das der Webserver zeigt, der Webserver ist, veranlasst Flutter, einen Webserver auf localhost:<RANDOM_PORT> zu starten, wodurch Sie auf Ihr Flutter zugreifen können Web-App von jedem Browser aus.

Wenn Sie Chrome installiert haben, es aber nicht angezeigt wird, müssen Sie die Umgebungsvariable CHROME_EXECUTABLE auf den Pfad zur ausführbaren Chrome-Datei setzen.

Ausführen und Erstellen von Flutter-Desktop-Apps

Nachdem Sie die Flutter-Desktop-Unterstützung aktiviert haben, können Sie eine Flutter-App nativ auf Ihrer Entwicklungs-Workstation mit flutter run -d <OS_NAME> , wobei <OS_NAME> durch denselben Wert ersetzen, den Sie beim Aktivieren der Desktop-Unterstützung verwendet haben. Sie können mit flutter build <OS_NAME> auch Binärdateien im build -Verzeichnis erstellen.

Bevor Sie dies tun können, benötigen Sie jedoch ein Verzeichnis, das enthält, was Flutter für Ihre Plattform erstellen muss. Dies wird automatisch erstellt, wenn Sie ein neues Projekt erstellen, aber Sie müssen es für ein vorhandenes Projekt mit flutter create . . Außerdem sind die Linux- und Windows-APIs instabil, sodass Sie sie möglicherweise für diese Plattformen neu generieren müssen, wenn die App nach einem Flutter-Update nicht mehr funktioniert.

Wann ist eine App kompatibel?

Was habe ich die ganze Zeit gemeint, als ich erwähnte, dass eine Flutter-App ein „kompatibles Projekt“ sein muss, damit sie auf dem Desktop oder im Web funktioniert? Einfach gesagt, ich meine, dass es kein Plugin verwenden darf, das keine plattformspezifische Implementierung für die Plattform hat, auf der Sie aufbauen möchten.

Um diesen Punkt jedem absolut klar zu machen und Missverständnisse zu vermeiden, beachten Sie bitte, dass ein Flutter-Plugin ein bestimmtes Flutter-Paket ist, das plattformspezifischen Code enthält, der für die Bereitstellung seiner Funktionen erforderlich ist.

Beispielsweise können Sie das von Google entwickelte Paket url_launcher so oft verwenden, wie Sie möchten (und Sie möchten dies vielleicht, da das Web auf Hyperlinks aufgebaut ist).

Ein Beispiel für ein von Google entwickeltes Paket, dessen Verwendung die Webentwicklung ausschließen würde, ist path_provider , das verwendet wird, um den lokalen Speicherpfad zum Speichern von Dateien abzurufen. Dies ist ein Beispiel für ein Paket, das übrigens für eine Web-App nicht von Nutzen ist. Es ist also nicht wirklich schlimm, es nicht verwenden zu können, außer der Tatsache, dass Sie Ihren Code ändern müssen, damit es funktioniert es im Web zu arbeiten, wenn Sie es verwenden.

Beispielsweise können Sie das Paket shared_preferences verwenden, das auf HTML localStorage im Web basiert.

Ähnliche Einschränkungen gelten für Desktop-Plattformen: Nur sehr wenige Plugins sind mit Desktop-Plattformen kompatibel, und da dies ein wiederkehrendes Thema ist, muss auf der Desktop-Seite viel mehr daran gearbeitet werden, als bei Flutter für das Web wirklich notwendig ist.

Responsive Layouts in Flutter erstellen

Aufgrund dessen, was ich oben beschrieben habe, und der Einfachheit halber gehe ich für den Rest dieses Beitrags davon aus, dass Ihre Zielplattform das Web ist, aber die grundlegenden Konzepte gelten auch für die Desktop-Entwicklung.

Die Unterstützung des Internets hat Vorteile und Pflichten. Ziemlich gezwungen zu sein, unterschiedliche Bildschirmgrößen zu unterstützen, mag nach einem Nachteil klingen, aber bedenken Sie, dass Sie durch das Ausführen der App in den Webbrowsern sehr einfach sehen können, wie Ihre App auf Bildschirmen mit unterschiedlichen Größen und Seitenverhältnissen aussehen wird, ohne sie separat ausführen zu müssen Emulatoren für Mobilgeräte.

Lassen Sie uns jetzt über Code sprechen. Wie können Sie Ihre App responsive machen?

Es gibt zwei Perspektiven, aus denen diese Analyse durchgeführt wird:

  1. „Welche Widgets verwende ich oder kann ich verwenden, die sich an Bildschirme unterschiedlicher Größe anpassen können oder sollten?“
  2. „Wie kann ich Informationen über die Größe des Bildschirms erhalten und wie kann ich sie beim Schreiben von UI-Code verwenden?“

Die erste Frage beantworten wir später. Lassen Sie uns zuerst über letzteres sprechen, weil es sehr einfach zu handhaben ist und den Kern des Problems darstellt. Dazu gibt es zwei Möglichkeiten:

  1. Eine Möglichkeit besteht darin, die Informationen aus den MediaQueryData des MediaQuery -Stamms InheritedWidget zu entnehmen, die im Widget-Baum vorhanden sein müssen, damit eine Flutter-App funktioniert (sie ist Teil von MaterialApp/WidgetsApp/CupertinoApp ), die Sie erhalten können, genau wie jedes andere InheritedWidget mit MediaQuery.of(context) , das eine size -Eigenschaft hat, die vom Typ Size ist, und die daher zwei width - und height -Eigenschaften vom Typ double hat.
  2. Die andere Möglichkeit besteht darin, einen LayoutBuilder zu verwenden, bei dem es sich um ein Builder-Widget (genau wie ein StreamBuilder oder ein FutureBuilder ) handelt, das (zusammen mit dem context ) ein BoxConstraints Objekt mit den Eigenschaften minHeight , maxHeight , minWidth und maxWidth an die builder -Funktion übergibt.

Hier ist ein Beispiel-DartPad, das die MediaQuery , um Einschränkungen zu erhalten, der Code dafür ist der folgende:

 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 ) ] ) ) ); }

Und hier ist einer, der den LayoutBuilder für dasselbe verwendet:

 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 ) ] ) ) ) ); }

Lassen Sie uns nun darüber nachdenken, welche Widgets sich an die Einschränkungen anpassen können.

Lassen Sie uns zunächst über die verschiedenen Möglichkeiten nachdenken, mehrere Widgets entsprechend der Größe des Bildschirms anzuordnen.

Das Widget, das sich am einfachsten anpasst, ist GridView . Tatsächlich benötigt eine GridView , die mit dem GridView.extent Konstruktor erstellt wurde, nicht einmal Ihre Beteiligung, um responsiv zu sein, wie Sie in diesem sehr einfachen Beispiel sehen können:

 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() ) ); }

Sie können Inhalte unterschiedlicher Größe berücksichtigen, indem Sie maxCrossAxisExtent .

Dieses Beispiel diente hauptsächlich dazu, die Existenz des GridView.extent GridView Konstruktors zu zeigen, aber eine viel intelligentere Methode wäre die Verwendung eines GridView.builder mit einem SliverGridDelegateWithMaxCrossAxisExtent , in diesem Fall, wo die Widgets im Raster angezeigt werden sollen werden dynamisch aus einer anderen Datenstruktur erstellt, wie Sie in diesem Beispiel sehen können:

 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]) ) ) ) ) ); }

Ein Beispiel für die Anpassung von GridView an verschiedene Bildschirme ist meine persönliche Zielseite, eine sehr einfache Flutter-Webanwendung, die aus einer GridView mit einer Reihe von Cards besteht, genau wie der vorherige Beispielcode, außer dass die Cards etwas komplexer und größer sind .

Eine sehr einfache Änderung, die an für Telefone entwickelten Apps vorgenommen werden könnte, wäre das Ersetzen einer Drawer durch ein permanentes Menü auf der linken Seite, wenn Platz vorhanden ist.

Zum Beispiel könnten wir eine ListView von Widgets wie die folgende haben, die für die Navigation verwendet wird:

 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"), ) ) ] ); }

Auf einem Smartphone ist dies ein üblicher Ort, der sich in einer Drawer befindet (auch bekannt als Hamburger-Menü).

Alternativen dazu wären die BottomNavigationBar oder die TabBar in Kombination mit der TabBarView , aber bei beiden müssten wir mehr Änderungen vornehmen als bei der Schublade nötig sind, also bleiben wir bei der Schublade.

Um nur den Drawer , der das Menu enthält, das wir zuvor auf kleineren Bildschirmen gesehen haben, würden Sie Code schreiben, der wie der folgende Ausschnitt aussieht, die Breite mit MediaQuery.of(context) überprüfen und ein Drawer -Objekt nur dann an das Scaffold übergeben, wenn dies der Fall ist weniger als ein Breitenwert, der unserer Meinung nach für unsere App geeignet ist:

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

Lassen Sie uns nun über den body des Scaffold nachdenken. Als Beispiel für den Hauptinhalt unserer App verwenden wir die zuvor GridView , die wir in einem separaten Widget namens Content aufbewahren, um Verwirrung zu vermeiden:

 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]) ) ) ) ); }

Auf größeren Bildschirmen kann der Hauptteil selbst eine Row sein, die zwei Widgets anzeigt: das Menu , das auf eine feste Breite beschränkt ist, und den Content , der den Rest des Bildschirms ausfüllt.

Auf kleineren Bildschirmen wäre der gesamte body der Content .

Wir packen alles in ein SafeArea und ein Center -Widget, da Flutter-Web-App-Widgets, insbesondere bei Verwendung von Row s und Column s, manchmal außerhalb des sichtbaren Bildschirmbereichs landen, und das wird mit SafeArea und/oder Center behoben.

Dies bedeutet, dass der body des Scaffold wie folgt aussehen wird:

 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() ) ] ) ) )

Hier ist das alles zusammengefasst:

(Große Vorschau)
 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]) ) ) ) ); }

Dies ist das meiste, was Sie als allgemeine Einführung in die reaktionsschnelle Benutzeroberfläche in Flutter benötigen. Ein Großteil der Anwendung hängt von der spezifischen Benutzeroberfläche Ihrer App ab, und es ist schwierig, genau festzulegen, was Sie tun können, um Ihre App reaktionsfähig zu machen, und Sie können je nach Ihren Vorlieben viele Ansätze verfolgen. Lassen Sie uns nun sehen, wie wir ein vollständigeres Beispiel in eine reaktionsschnelle App umwandeln können, indem wir über allgemeine App-Elemente und UI-Flows nachdenken.

Um es in den Kontext zu stellen: Eine App reaktionsfähig machen

Bisher haben wir nur einen Bildschirm. Lassen Sie uns das zu einer Zwei-Bildschirm-App mit funktionierender URL-basierter Navigation erweitern!

Erstellen einer responsiven Anmeldeseite

Wahrscheinlich hat Ihre App eine Anmeldeseite. Wie können wir das responsiv machen?

Anmeldebildschirme auf mobilen Geräten sind sich normalerweise ziemlich ähnlich. Der verfügbare Platz ist nicht viel; Es ist normalerweise nur eine Column mit etwas Padding um die Widgets und enthält TextField s zum Eingeben eines Benutzernamens und eines Passworts sowie eine Schaltfläche zum Anmelden. Also ein ziemlicher Standard (obwohl er nicht funktioniert, da dies unter anderem erforderlich wäre). , ein TextEditingController für jedes TextField ) Anmeldeseite für eine mobile App könnte wie folgt aussehen:

 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: () {} ) ] ), ), )

Auf einem mobilen Gerät sieht es gut aus, aber diese sehr breiten TextField s beginnen auf einem Tablet, geschweige denn auf einem größeren Bildschirm, unruhig auszusehen. Wir können uns jedoch nicht einfach für eine feste Breite entscheiden, da Telefone unterschiedliche Bildschirmgrößen haben und wir uns ein gewisses Maß an Flexibilität bewahren sollten.

Durch Experimentieren könnten wir beispielsweise feststellen, dass die maximale Breite 500 betragen sollte. Nun, wir würden die constraints des Container auf 500 setzen (ich habe im vorherigen Beispiel einen Container anstelle von Padding verwendet, weil ich wusste, wohin ich damit wollte ) und wir können loslegen, oder? Nicht wirklich, denn das würde dazu führen, dass die Login-Widgets an der linken Seite des Bildschirms hängen bleiben, was noch schlimmer wäre, als alles zu dehnen. Also packen wir ein Center -Widget wie folgt ein:

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

Das sieht schon gut aus, und wir mussten weder einen LayoutBuilder noch die MediaQuery.of(context).size . Lassen Sie uns noch einen Schritt weiter gehen, damit dies sehr gut aussieht. Meiner Meinung nach würde es besser aussehen, wenn der Vordergrundteil irgendwie vom Hintergrund getrennt wäre. Wir können dies erreichen, indem wir dem, was sich hinter dem Container befindet, mit den Eingabe-Widgets eine Hintergrundfarbe geben und den Container im Vordergrund weiß lassen. Um es ein wenig besser aussehen zu lassen, lassen Sie uns verhindern, dass sich der Container auf großen Geräten nach oben und unten auf dem Bildschirm ausdehnt, ihm abgerundete Ecken geben und ihm einen schönen animierten Übergang zwischen den beiden Layouts geben.

All das erfordert jetzt einen LayoutBuilder und einen äußeren Container , um sowohl eine Hintergrundfarbe festzulegen als auch Padding rund um den Container hinzuzufügen und nicht nur an den Seiten nur auf größeren Bildschirmen. Um die Änderung des Auffüllbetrags animieren zu können, müssen wir diesen äußeren Container auch nur in einen AnimatedContainer , was eine duration für die Animation erfordert, die wir auf eine halbe Sekunde festlegen, was Duration(milliseconds: 500) in ist Code.

Hier ist das Beispiel einer responsiven Anmeldeseite:

(Große Vorschau)
 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() ) ); } ) ] ), ), ) ); } ) ); }

Wie Sie sehen können, habe ich auch den RaisedButton von onPressed in einen Callback geändert, der uns zu einem Bildschirm namens HomePage navigiert (was beispielsweise die Ansicht sein könnte, die wir zuvor mit einer GridView und einem Menü oder einer Schublade erstellt haben). Jetzt konzentrieren wir uns jedoch auf diesen Navigationsteil.

Benannte Routen: Machen Sie die Navigation Ihrer App mehr wie eine richtige Web-App

Eine häufige Eigenschaft von Web-Apps ist die Möglichkeit, Bildschirme basierend auf der URL zu ändern. Wenn Sie beispielsweise zu https://appurl/login gehen, sollten Sie etwas anderes erhalten als https://appurl/somethingelse . Tatsächlich unterstützt Flutter benannte Routen , die zwei Zwecke haben:

  1. In einer Web-App haben sie genau das Feature, das ich im vorherigen Satz erwähnt habe.
  2. In jeder App ermöglichen sie es Ihnen, Routen für Ihre App vorzudefinieren und ihnen Namen zu geben und dann in der Lage zu sein, zu ihnen zu navigieren, indem Sie einfach ihren Namen angeben.

Dazu müssen wir den MaterialApp -Konstruktor in einen ändern, der wie folgt aussieht:

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

Und dann können wir zu einer anderen Route wechseln, indem wir Navigator.pushNamed(context, routeName) und Navigator.pushReplacementNamed(context, routeName) anstelle von Navigator.push(context, route) und Navigator.pushReplacement(context, route) verwenden.

Dies gilt für die hypothetische App, die wir im Rest dieses Artikels erstellt haben. Sie können benannte Routen in DartPad nicht wirklich in Aktion sehen, also sollten Sie dies auf Ihrem eigenen Computer mit flutter run ausprobieren oder das Beispiel in Aktion überprüfen:

(Große Vorschau)
 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]) ) ) ) ); }

Weiter mit deinem Flutter-Abenteuer

Das sollte Ihnen eine Vorstellung davon geben, was Sie mit Flutter auf größeren Bildschirmen machen können, insbesondere im Internet. Es ist ein schönes Framework, sehr einfach zu bedienen, und seine extreme plattformübergreifende Unterstützung macht es nur noch wichtiger, es zu lernen und mit der Verwendung zu beginnen. Also, fangen Sie an, Flutter auch für Web-Apps zu vertrauen!

Weitere Ressourcen

  • „Desktop-Shells“, GitHub
    Der aktuelle, immer aktuelle Stand von Flutter auf dem Desktop
  • „Desktop-Unterstützung für Flutter“, Flutter
    Informationen zu den vollständig unterstützten Desktop-Plattformen
  • „Webunterstützung für Flutter“, Flutter
    Informationen zu Flutter für das Web
  • „Alle Samples“, Flutter
    Eine kuratierte Liste von Flutter-Beispielen und -Apps