Dezvoltare web și desktop receptivă cu Flutter

Publicat: 2022-03-10
Rezumat rapid ↬ Flutter a făcut deja o explozie pe scena dezvoltării mobile. Acum acceptă și dispozitive mai mari. Iată ce trebuie să știți pentru a fi gata să vă asumați sarcina de a dezvolta aplicații web și desktop folosind acest minunat cadru multiplatform.

Acest tutorial nu este o introducere în Flutter în sine. Există o mulțime de articole, videoclipuri și mai multe cărți disponibile online, cu introduceri simple care vă vor ajuta să învățați elementele de bază ale Flutter. În schimb, vom acoperi următoarele două obiective:

  1. Starea actuală a dezvoltării non-mobile Flutter și modul în care puteți rula codul Flutter în browser, pe un computer desktop sau laptop;
  2. Cum să creați aplicații responsive folosind Flutter, astfel încât să puteți vedea puterea sa - în special ca cadru web - pe afișaj complet, care se termină cu o notă despre rutare bazată pe URL.

Să intrăm în asta!

Ce este flutterul, de ce este important, în ce a evoluat, unde se duce

Flutter este cel mai recent cadru de dezvoltare de aplicații de la Google. Google se gândește să fie atotcuprinzător: va permite executarea aceluiași cod pe smartphone-uri de toate mărcile, pe tablete și pe computer desktop și laptop ca aplicații native sau ca pagini web.

Este un proiect foarte ambițios, dar Google a avut un succes incredibil până acum, în special în două aspecte: în crearea unui cadru cu adevărat independent de platformă pentru aplicațiile native Android și iOS, care funcționează excelent și este complet pregătit pentru utilizare în producție și în crearea unui front impresionant. -end cadru web care poate partaja 100% din cod cu o aplicație compatibilă Flutter.

În secțiunea următoare, vom vedea ce face aplicația compatibilă și care este stadiul dezvoltării Flutter non-mobile de acum.

Mai multe după săritură! Continuați să citiți mai jos ↓

Dezvoltare non-mobilă cu Flutter

Dezvoltarea non-mobilă cu Flutter a fost publicată pentru prima dată într-un mod semnificativ la Google I/O 2019. Această secțiune este despre cum să funcționeze și despre când funcționează.

Cum să activați dezvoltarea web și desktop

Pentru a activa dezvoltarea web, trebuie să fiți mai întâi pe canalul beta al Flutter. Există două moduri de a ajunge la acest punct:

  • Instalați Flutter direct pe canalul beta, descărcând cea mai recentă versiune beta corespunzătoare din arhiva SDK.
  • Dacă aveți deja instalat Flutter, comutați la canalul beta cu $ flutter channel beta și apoi efectuați comutarea prin actualizarea versiunii dvs. Flutter (care este de fapt un git pull în folderul de instalare Flutter) cu $ flutter upgrade .

După aceea, puteți rula asta:

 $ flutter config --enable-web

Suportul pentru desktop este mult mai experimental, mai ales din cauza lipsei de instrumente pentru Linux și Windows, ceea ce face ca dezvoltarea de pluginuri să fie mai ales o problemă majoră și datorită faptului că API-urile utilizate pentru aceasta sunt destinate utilizării de dovadă a conceptului și nu pentru producție. Acest lucru este spre deosebire de dezvoltarea web, care utilizează compilatorul dart2js încercat și testat pentru versiuni de versiuni, care nici măcar nu sunt acceptate pentru aplicațiile desktop native Windows și Linux.

Notă : Suportul pentru macOS este puțin mai bun decât suportul pentru Windows și Linux, dar încă nu este la fel de bun ca suportul pentru web și nici pe departe la fel de bun ca suportul complet pentru platformele mobile.

Pentru a activa suportul pentru dezvoltarea desktop, trebuie să comutați la canalul de lansare master urmând aceiași pași descriși mai devreme pentru canalul beta . Apoi, rulați următoarele, înlocuind <OS_NAME> fie cu linux , windows , fie macos :

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

În acest moment, dacă aveți probleme cu oricare dintre următorii pași pe care îi voi descrie, deoarece instrumentul Flutter nu face ceea ce spun eu că ar trebui să facă, câțiva pași obișnuiți de depanare sunt următorii:

  • Rulați flutter doctor pentru a verifica probleme. Un efect secundar al acestei comenzi Flutter este că ar trebui să descarce orice instrumente de care are nevoie și pe care nu le are.
  • Rulați flutter upgrade .
  • Opriți-l și porniți-l din nou. Vechiul răspuns de asistență tehnică de nivel 1 de repornire a computerului ar putea fi exact ceea ce este necesar pentru a vă putea bucura de toată bogăția Flutter.

Rularea și construirea de aplicații web Flutter

Suportul web Flutter nu este deloc rău, iar acest lucru se reflectă în ușurința de dezvoltare pentru web.

Rularea asta...

 $ flutter devices

… ar trebui să arate imediat o intrare pentru ceva de genul acesta:

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

În plus, rularea browserului Chrome ar trebui să determine Flutter să afișeze și o intrare pentru acesta. Rularea flutter run pe un proiect Flutter compatibil (mai multe despre asta mai târziu) când singurul „dispozitiv conectat” care apare este serverul web va determina Flutter să pornească un server web pe localhost:<RANDOM_PORT> , ceea ce vă va permite să vă accesați Flutter. aplicație web din orice browser.

Dacă ați instalat Chrome, dar nu apare, trebuie să setați variabila de mediu CHROME_EXECUTABLE la calea către fișierul executabil Chrome.

Rularea și crearea de aplicații desktop Flutter

După ce ați activat suportul pentru desktop Flutter, puteți rula o aplicație Flutter nativ pe stația de lucru de dezvoltare cu flutter run -d <OS_NAME> , înlocuind <OS_NAME> cu aceeași valoare pe care ați folosit-o când ați activat suportul pentru desktop. De asemenea, puteți crea fișiere binare în directorul de compilare cu flutter build <OS_NAME> build

Înainte de a putea face orice, totuși, trebuie să aveți un director care să conțină ceea ce Flutter trebuie să construiască pentru platforma dvs. Acesta va fi creat automat când creați un proiect nou, dar va trebui să îl creați pentru un proiect existent cu flutter create . . De asemenea, API-urile Linux și Windows sunt instabile, așa că ar putea fi necesar să le regenerați pentru acele platforme dacă aplicația nu mai funcționează după o actualizare Flutter.

Când este compatibilă o aplicație?

Ce am vrut să spun tot timpul când am menționat că o aplicație Flutter trebuie să fie un „proiect compatibil” pentru ca aceasta să funcționeze pe desktop sau pe web? Pur și simplu, vreau să spun că nu trebuie să folosească niciun plugin care nu are o implementare specifică platformei pentru platforma pe care încercați să construiți.

Pentru a face acest punct absolut clar pentru toată lumea și pentru a evita neînțelegerile, vă rugăm să rețineți că un plugin Flutter este un pachet special Flutter care conține cod specific platformei care este necesar pentru ca acesta să-și ofere caracteristicile.

De exemplu, puteți folosi pachetul url_launcher dezvoltat de Google atât cât doriți (și poate doriți, având în vedere că web-ul este construit pe hyperlinkuri).

Un exemplu de pachet dezvoltat de Google a cărui utilizare ar împiedica dezvoltarea web este path_provider , care este folosit pentru a obține calea de stocare locală pentru a salva fișierele. Acesta este un exemplu de pachet care, de altfel, nu este de nici un folos unei aplicații web, așa că a nu-l putea folosi nu este chiar o dezamăgire, cu excepția faptului că trebuie să vă schimbați codul pentru a să funcționeze pe web dacă îl utilizați.

De exemplu, puteți utiliza pachetul shared_preferences, care se bazează pe HTML localStorage pe web.

Avertismente similare sunt valabile în ceea ce privește platformele desktop: Foarte puține plugin-uri sunt compatibile cu platformele desktop și, deoarece aceasta este o temă recurentă, trebuie făcută mult mai mult lucru în acest sens pe partea desktop decât este cu adevărat necesar pe Flutter pentru web.

Crearea unor aspecte receptive în Flutter

Din cauza a ceea ce am descris mai sus și pentru simplitate, voi presupune pentru restul acestei postări că platforma dvs. țintă este web-ul, dar conceptele de bază se aplică și dezvoltării desktop.

Sprijinirea web-ului are beneficii și responsabilități. A fi aproape forțat să accepte diferite dimensiuni de ecran ar putea părea un dezavantaj, dar luați în considerare faptul că rularea aplicației în browserele web vă permite să vedeți foarte ușor cum va arăta aplicația dvs. pe ecrane de diferite dimensiuni și raporturi de aspect, fără a fi nevoie să rulați separat. emulatori de dispozitive mobile.

Acum, să vorbim despre cod. Cum puteți face aplicația dvs. receptivă?

Există două perspective din care se face această analiză:

  1. „Ce widget-uri folosesc sau pot folosi care se pot adapta sau ar trebui să se adapteze la ecrane de diferite dimensiuni?”
  2. „Cum pot obține informații despre dimensiunea ecranului și cum le pot folosi când scriu codul UI?”

Vom răspunde la prima întrebare mai târziu. Să vorbim mai întâi despre acesta din urmă, pentru că poate fi tratat foarte ușor și se află în centrul problemei. Există două moduri de a face acest lucru:

  1. O modalitate este să luați informațiile din MediaQueryData ale rădăcinii MediaQuery InheritedWidget care trebuie să existe în arborele widget pentru ca o aplicație Flutter să funcționeze (face parte din MaterialApp/WidgetsApp/CupertinoApp ), pe care o puteți obține, la fel ca orice alt InheritedWidget , cu MediaQuery.of(context) , care are o proprietate size , care este de tip Size , și care, prin urmare, are două proprietăți de width și height de tip double .
  2. Cealaltă modalitate este să utilizați un LayoutBuilder , care este un widget de constructor (la fel ca un StreamBuilder sau un FutureBuilder ) care trece la funcția de builder (împreună cu context ) un obiect BoxConstraints care are minHeight , maxHeight , minWidth și maxWidth .

Iată un exemplu de DartPad care utilizează MediaQuery pentru a obține constrângeri, al cărui cod este următorul:

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

Și iată unul care folosește LayoutBuilder pentru același lucru:

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

Acum, să ne gândim la ce widget-uri se pot adapta la constrângeri.

În primul rând, să ne gândim la diferitele moduri de aranjare a mai multor widget-uri în funcție de dimensiunea ecranului.

Widgetul care se adaptează cel mai ușor este GridView . De fapt, un GridView construit folosind constructorul GridView.extent nici măcar nu are nevoie de implicarea dvs. pentru a fi receptiv, așa cum puteți vedea în acest exemplu foarte simplu:

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

Puteți acomoda conținut de diferite dimensiuni prin modificarea maxCrossAxisExtent .

Acest exemplu a servit în cea mai mare parte scopului de a arăta existența constructorului GridView.extent GridView , dar o modalitate mult mai inteligentă de a face acest lucru ar fi utilizarea unui GridView.builder cu un SliverGridDelegateWithMaxCrossAxisExtent , în acest caz în care widget-urile vor fi afișate în grilă sunt create dinamic dintr-o altă structură de date, așa cum puteți vedea în acest exemplu:

 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 exemplu de adaptare GridView la diferite ecrane este pagina mea personală de destinație, care este o aplicație web Flutter foarte simplă, constând dintr-un GridView cu o grămadă de Cards , la fel ca exemplul de cod anterior, cu excepția faptului că Cards sunt puțin mai complexe și mai mari. .

O modificare foarte simplă care ar putea fi făcută aplicațiilor concepute pentru telefoane ar fi înlocuirea unui Drawer cu un meniu permanent în stânga atunci când există spațiu.

De exemplu, am putea avea un ListView de widget-uri, precum următorul, care este folosit pentru navigare:

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

Pe un smartphone, un loc obișnuit de utilizat, care ar fi în interiorul unui Drawer (cunoscut și sub numele de meniu de hamburger).

Alternative la aceasta ar fi BottomNavigationBar sau TabBar , în combinație cu TabBarView , dar cu ambele ar trebui să facem mai multe modificări decât sunt necesare cu sertarul, așa că vom rămâne cu sertarul.

Pentru a afișa doar Drawer care conține Menu pe care l-am văzut mai devreme pe ecrane mai mici, ai scrie cod care arată ca următorul fragment, verificând lățimea folosind MediaQuery.of(context) și trecând un obiect Drawer la Scaffold numai dacă este mai mică decât o valoare a lățimii pe care o considerăm potrivită pentru aplicația noastră:

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

Acum, să ne gândim la body Scaffold . Ca exemplu de conținut principal al aplicației noastre, vom folosi GridView pe care l-am creat anterior, pe care îl păstrăm într-un widget separat numit Content pentru a evita confuzia:

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

Pe ecrane mai mari, corpul în sine poate fi un Row care afișează două widget-uri: Menu , care este limitat la o lățime fixă ​​și Content care umple restul ecranului.

Pe ecranele mai mici, întregul body ar fi Content .

Vom împacheta totul într-un SafeArea și Center , deoarece, uneori, widget-urile aplicației web Flutter, în special atunci când utilizați Row s și Column s, ajung în afara zonei vizibile a ecranului, iar acest lucru este remediat cu SafeArea și/sau Center .

Aceasta înseamnă că body Scaffold va fi următorul:

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

Iată toate acestea reunite:

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

Acestea sunt cele mai multe dintre lucrurile de care veți avea nevoie ca o introducere generală la interfața de utilizare receptivă în Flutter. O mare parte din aplicația sa va depinde de interfața de utilizare specifică aplicației dvs. și este greu de identificat exact ce puteți face pentru ca aplicația să răspundă și puteți lua multe abordări în funcție de preferințele dvs. Acum, totuși, să vedem cum putem face un exemplu mai complet într-o aplicație receptivă, gândindu-ne la elementele comune ale aplicației și la fluxurile UI.

Punerea în context: adaptarea unei aplicații

Până acum, avem doar un ecran. Să extindem asta într-o aplicație cu două ecrane cu navigare funcțională bazată pe URL!

Crearea unei pagini de conectare receptive

Sunt șanse ca aplicația dvs. să aibă o pagină de conectare. Cum putem face acest lucru receptiv?

Ecranele de conectare de pe dispozitivele mobile sunt destul de asemănătoare între ele, de obicei. Spațiul disponibil nu este mult; de obicei, este doar o Column cu niște Padding în jurul widget-urilor și conține TextField -uri pentru introducerea unui nume de utilizator și a unei parole și un buton pentru a vă autentifica. Deci, un standard destul de (deși nu funcționează, deoarece asta ar necesita, printre altele, , un TextEditingController pentru fiecare pagină de conectare TextField ) pentru o aplicație mobilă ar putea fi următorul:

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

Arată bine pe un dispozitiv mobil, dar acele TextField foarte largi încep să arate șocante pe o tabletă, darămite un ecran mai mare. Cu toate acestea, nu putem decide doar o lățime fixă, deoarece telefoanele au dimensiuni diferite ale ecranului și ar trebui să menținem un grad de flexibilitate.

De exemplu, prin experimentare, am putea descoperi că lățimea maximă ar trebui să fie de 500. Ei bine, am seta constraints Container la 500 (am folosit un Container în loc de Padding în exemplul anterior pentru că știam unde mă îndrept cu asta ) și suntem gata de plecare, nu? Nu chiar, pentru că asta ar face ca widgeturile de conectare să rămână în partea stângă a ecranului, ceea ce ar putea fi chiar mai rău decât întinderea totul. Deci, includem un widget Center , astfel:

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

Deja arată bine și nici măcar nu a trebuit să folosim nici un LayoutBuilder , nici MediaQuery.of(context).size . Să facem un pas mai departe pentru a face acest lucru să arate foarte bine, totuși. Ar arăta mai bine, în opinia mea, dacă partea din prim-plan ar fi într-un fel separată de fundal. Putem realiza acest lucru dând o culoare de fundal a ceea ce se află în spatele Container cu widget-urile de intrare și păstrând Container din prim-plan alb. Pentru a-l face să arate puțin mai bine, să împiedicăm Container să se întindă în partea de sus și de jos a ecranului pe dispozitive mari, să-i dăm colțuri rotunjite și să-i oferim o tranziție animată plăcută între cele două aspecte.

Toate acestea necesită acum un LayoutBuilder și un Container exterior, atât pentru a seta o culoare de fundal, cât și pentru a adăuga umplutură în jurul Container și nu doar pe părțile laterale doar pe ecranele mai mari. De asemenea, pentru a face modificarea cantității de umplutură animată, trebuie doar să transformăm acel Container exterior într-un AnimatedContainer , care necesită o duration pentru animație, pe care o vom seta la jumătate de secundă, care este Duration(milliseconds: 500) în cod.

Iată acel exemplu de pagină de conectare receptivă:

(Previzualizare mare)
 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() ) ); } ) ] ), ), ) ); } ) ); }

După cum puteți vedea, am schimbat și butonul RaisedButton onPressed într-un apel invers care ne conduce la un ecran numit HomePage (care ar putea fi, de exemplu, vizualizarea pe care am construit-o anterior cu un GridView și un meniu sau un sertar). Acum, totuși, acea parte de navigare este pe care ne vom concentra.

Rute denumite: faceți ca navigarea aplicației dvs. să fie mai asemănătoare cu o aplicație web adecvată

Un lucru comun pe care îl au aplicațiile web este capacitatea de a schimba ecranele pe baza adresei URL. De exemplu, accesarea https://appurl/login ar trebui să vă ofere ceva diferit de https://appurl/somethingelse . Flutter, de fapt, acceptă rute numite , care au două scopuri:

  1. Într-o aplicație web, au exact acea caracteristică pe care am menționat-o în propoziția anterioară.
  2. În orice aplicație, acestea vă permit să predefiniți rute pentru aplicația dvs. și să le dați nume, apoi să puteți naviga la ele doar specificând numele lor.

Pentru a face acest lucru, trebuie să schimbăm constructorul MaterialApp cu unul care arată astfel:

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

Și apoi putem comuta la o altă rută utilizând Navigator.pushNamed(context, routeName) și Navigator.pushReplacementNamed(context, routeName) , în loc de Navigator.push(context, route) și Navigator.pushReplacement(context, route) .

Iată cea aplicată aplicației ipotetice pe care am construit-o în restul acestui articol. Nu puteți vedea rutele numite în acțiune în DartPad, așa că ar trebui să încercați acest lucru pe propria mașină cu flutter run sau să verificați exemplul în acțiune:

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

Mai departe cu aventura ta Flutter

Asta ar trebui să vă dea o idee despre ce puteți face cu Flutter pe ecrane mai mari, în special pe web. Este un cadru minunat, foarte ușor de utilizat, iar suportul său extrem de multiplatformă face doar mai esențial să înveți și să începi să folosești. Deci, continuă și începe să ai încredere și în Flutter pentru aplicațiile web!

Resurse suplimentare

  • „Cochilii de desktop”, GitHub
    Starea actuală, mereu actualizată, a Flutter pe desktop
  • „Suport desktop pentru Flutter”, Flutter
    Informații despre platformele desktop complet acceptate
  • „Suport web pentru Flutter”, Flutter
    Informații despre Flutter pentru web
  • „Toate mostrele”, Flutter
    O listă îngrijită de mostre și aplicații Flutter