Responsywne tworzenie stron internetowych i pulpitu z Flutter

Opublikowany: 2022-03-10
Szybkie podsumowanie ↬ Flutter zrobił już niemałą furorę na mobilnej scenie programistycznej. Teraz działa również na większych urządzeniach. Oto, co musisz wiedzieć, aby być gotowym do podjęcia zadania tworzenia aplikacji internetowych i desktopowych przy użyciu tego wspaniałego, wieloplatformowego frameworka.

Ten samouczek nie jest wprowadzeniem do samego Fluttera. Istnieje wiele artykułów, filmów i kilka książek dostępnych online z prostymi wstępami, które pomogą Ci poznać podstawy Fluttera. Zamiast tego omówimy następujące dwa cele:

  1. Aktualny stan rozwoju oprogramowania Flutter na urządzenia mobilne i sposób uruchamiania kodu Flutter w przeglądarce, na komputerze stacjonarnym lub laptopie;
  2. Jak tworzyć responsywne aplikacje za pomocą Fluttera, aby móc zobaczyć jego moc – zwłaszcza jako framework webowy – na pełnym ekranie, kończąc na notatce o routingu opartym na adresach URL.

Wejdźmy w to!

Co to jest trzepotanie, dlaczego jest ważne, w co ewoluowało, dokąd zmierza

Flutter to najnowsza platforma Google do tworzenia aplikacji. Google wyobraża sobie, że będzie wszechogarniający: umożliwi wykonanie tego samego kodu na smartfonach wszystkich marek, na tabletach oraz na komputerach stacjonarnych i laptopach jako aplikacje natywne lub strony internetowe.

To bardzo ambitny projekt, ale do tej pory Google odnosił niesamowite sukcesy, szczególnie w dwóch aspektach: w tworzeniu prawdziwie niezależnego od platformy frameworka dla aplikacji natywnych na Androida i iOS, który działa świetnie i jest w pełni gotowy do użytku produkcyjnego, oraz w tworzeniu imponującego frontu -end framework webowy, który może współdzielić 100% kodu z kompatybilną aplikacją Flutter.

W następnej sekcji zobaczymy, co sprawia, że ​​aplikacja jest kompatybilna i jaki jest obecnie stan niemobilnego rozwoju Fluttera.

Więcej po skoku! Kontynuuj czytanie poniżej ↓

Niemobilny rozwój z Flutter

Programowanie niemobilne z Flutterem zostało po raz pierwszy nagłośnione w znaczący sposób na Google I/O 2019. Ta sekcja dotyczy tego, jak sprawić, by działała i kiedy to działa.

Jak włączyć tworzenie stron internetowych i pulpitu?

Aby umożliwić tworzenie stron internetowych, musisz najpierw być na kanale beta Fluttera. Do tego momentu można dojść na dwa sposoby:

  • Zainstaluj Flutter bezpośrednio na kanale beta, pobierając odpowiednią najnowszą wersję beta z archiwum SDK.
  • Jeśli masz już zainstalowany Flutter, przełącz się na kanał beta za pomocą $ flutter channel beta , a następnie wykonaj samo przełączenie, aktualizując swoją wersję Fluttera (która w rzeczywistości jest git pull w folderze instalacyjnym Fluttera) za pomocą $ flutter upgrade .

Następnie możesz uruchomić to:

 $ flutter config --enable-web

Wsparcie dla komputerów stacjonarnych jest znacznie bardziej eksperymentalne, zwłaszcza ze względu na brak narzędzi dla systemów Linux i Windows, co sprawia, że ​​tworzenie wtyczek jest szczególnie uciążliwe, a także ze względu na fakt, że używane do tego interfejsy API są przeznaczone do użytku w celu sprawdzenia koncepcji, a nie do produkcja. Jest to w przeciwieństwie do tworzenia stron internetowych, które wykorzystuje wypróbowany i przetestowany kompilator dart2js do kompilacji wydań, które nie są nawet obsługiwane dla natywnych aplikacji komputerowych dla systemów Windows i Linux.

Uwaga : obsługa systemu macOS jest nieco lepsza niż obsługa systemów Windows i Linux, ale nadal nie jest tak dobra jak obsługa sieci WWW i nie jest tak dobra, jak pełna obsługa platform mobilnych.

Aby włączyć obsługę programowania komputerów stacjonarnych, musisz przełączyć się na kanał wersji master , wykonując te same czynności, które opisano wcześniej dla kanału beta . Następnie uruchom następujące polecenie, zastępując <OS_NAME> linux , windows lub macos :

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

W tym momencie, jeśli masz problemy z którymkolwiek z poniższych kroków, które opiszę, ponieważ narzędzie Flutter nie robi tego, co mówię, że powinno, niektóre typowe kroki rozwiązywania problemów są następujące:

  • Uruchom flutter doctor , aby sprawdzić problemy. Efektem ubocznym tego polecenia Flutter jest to, że powinno pobrać wszystkie potrzebne narzędzia, których nie ma.
  • Uruchom flutter upgrade .
  • Wyłącz i włącz ponownie. Stara odpowiedź wsparcia technicznego poziomu 1, polegająca na ponownym uruchomieniu komputera, może być właśnie tym, czego potrzebujesz, abyś mógł cieszyć się pełnymi bogactwami Fluttera.

Uruchamianie i tworzenie aplikacji internetowych Flutter

Obsługa sieciowa Flutter wcale nie jest zła, co znajduje odzwierciedlenie w łatwości tworzenia aplikacji internetowych.

Uruchamiam to…

 $ flutter devices

… powinien od razu pokazać wpis o czymś takim:

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

Ponadto uruchomienie przeglądarki Chrome powinno spowodować, że Flutter również wyświetli jej wpis. Uruchomienie flutter run na kompatybilnym projekcie Flutter (więcej o tym później), gdy jedynym wyświetlanym „podłączonym urządzeniem” jest serwer WWW, spowoduje, że Flutter uruchomi serwer WWW na localhost:<RANDOM_PORT> , co pozwoli ci uzyskać dostęp do twojego Fluttera aplikacja internetowa z dowolnej przeglądarki.

Jeśli zainstalowałeś Chrome, ale się nie wyświetla, musisz ustawić zmienną środowiskową CHROME_EXECUTABLE na ścieżkę do pliku wykonywalnego Chrome.

Uruchamianie i budowanie aplikacji na komputery stacjonarne Flutter

Po włączeniu obsługi pulpitu Flutter możesz uruchomić aplikację Flutter natywnie na swojej programistycznej stacji roboczej za pomocą flutter run -d <OS_NAME> , zastępując <OS_NAME> taką samą wartością, jaka została użyta podczas włączania obsługi pulpitu. Możesz także budować binaria w katalogu build za pomocą flutter build <OS_NAME> .

Zanim jednak będziesz mógł to zrobić, musisz mieć katalog zawierający to, co Flutter musi zbudować dla twojej platformy. Zostanie on utworzony automatycznie podczas tworzenia nowego projektu, ale musisz go utworzyć dla istniejącego projektu za pomocą funkcji flutter create . . Ponadto interfejsy API systemu Linux i Windows są niestabilne, więc może być konieczne ponowne ich wygenerowanie dla tych platform, jeśli aplikacja przestanie działać po aktualizacji Fluttera.

Kiedy aplikacja jest kompatybilna?

Co miałem na myśli przez cały czas, gdy wspominałem, że aplikacja Flutter musi być „kompatybilnym projektem”, aby działała na komputerze lub w Internecie? Mówiąc prościej, mam na myśli, że nie może używać żadnej wtyczki, która nie ma implementacji specyficznej dla platformy dla platformy, na której próbujesz zbudować.

Aby ten punkt był całkowicie jasny dla wszystkich i aby uniknąć nieporozumień, należy pamiętać, że wtyczka Flutter to szczególny pakiet Flutter, który zawiera kod specyficzny dla platformy, który jest niezbędny do zapewnienia jej funkcji.

Na przykład możesz używać pakietu url_launcher opracowanego przez Google tyle, ile chcesz (i możesz chcieć, biorąc pod uwagę, że sieć jest zbudowana na hiperłączach).

Przykładem pakietu opracowanego przez Google, którego użycie uniemożliwiłoby tworzenie stron internetowych, jest path_provider , który służy do pobierania lokalnej ścieżki pamięci masowej, w której mają być zapisywane pliki. To jest przykład pakietu, który, nawiasem mówiąc, nie jest w żaden sposób przydatny w aplikacji internetowej, więc niemożność korzystania z niego nie jest w rzeczywistości problemem, z wyjątkiem tego, że musisz zmienić swój kod, aby do pracy w sieci, jeśli jej używasz.

Na przykład możesz użyć pakietu shared_preferences, który opiera się na HTML localStorage w sieci.

Podobne zastrzeżenia dotyczą platform komputerowych: bardzo niewiele wtyczek jest zgodnych z platformami komputerowymi, a ponieważ jest to powtarzający się motyw, znacznie więcej pracy nad tym należy wykonać po stronie komputerów stacjonarnych, niż jest to naprawdę konieczne w przypadku Flutter dla sieci.

Tworzenie responsywnych układów w Flutter

Ze względu na to, co opisałem powyżej i dla uproszczenia, w dalszej części tego postu zamierzam założyć, że twoją platformą docelową jest sieć, ale podstawowe koncepcje dotyczą również tworzenia pulpitu.

Wspieranie sieci niesie ze sobą korzyści i obowiązki. Zmuszenie do obsługi różnych rozmiarów ekranu może wydawać się wadą, ale weź pod uwagę, że uruchomienie aplikacji w przeglądarkach internetowych pozwala bardzo łatwo zobaczyć, jak Twoja aplikacja będzie wyglądać na ekranach o różnych rozmiarach i proporcjach, bez konieczności uruchamiania osobno emulatory urządzeń mobilnych.

Teraz porozmawiajmy o kodzie. Jak sprawić, by Twoja aplikacja była responsywna?

Istnieją dwie perspektywy, z których przeprowadza się tę analizę:

  1. „Jakich widżetów używam lub czy mogę używać, które mogą lub powinny dostosować się do ekranów o różnych rozmiarach?”
  2. „Jak mogę uzyskać informacje o rozmiarze ekranu i jak mogę z nich korzystać podczas pisania kodu interfejsu użytkownika?”

Na pierwsze pytanie odpowiemy później. Porozmawiajmy najpierw o tym drugim, ponieważ można sobie z nim bardzo łatwo poradzić i jest on sednem sprawy. Można to zrobić na dwa sposoby:

  1. Jednym ze sposobów jest pobranie informacji z MediaQueryData głównego MediaQuery InheritedWidget , który musi istnieć w drzewie widżetów, aby aplikacja Flutter działała (jest częścią MaterialApp/WidgetsApp/CupertinoApp ), którą można uzyskać, podobnie jak dowolny inny InheritedWidget , z MediaQuery.of(context) , który ma właściwość size typu Size i w związku z tym ma dwie właściwości width i height typu double .
  2. Innym sposobem jest użycie LayoutBuilder , który jest widżetem konstruktora (podobnie jak StreamBuilder lub FutureBuilder ), który przekazuje do funkcji builder (wraz z context ) obiekt BoxConstraints , który ma minHeight , maxHeight , minWidth i maxWidth .

Oto przykład DartPad używającego MediaQuery do pobierania ograniczeń, którego kod jest następujący:

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

A oto jeden, który używa LayoutBuilder do tego samego:

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

Zastanówmy się teraz, jakie widżety mogą dostosować się do ograniczeń.

Przede wszystkim zastanówmy się nad różnymi sposobami rozmieszczenia wielu widżetów w zależności od rozmiaru ekranu.

Widżet, który najłatwiej się dostosowuje, to GridView . W rzeczywistości widok GridView zbudowany przy użyciu konstruktora GridView.extent nie wymaga nawet Twojego zaangażowania, aby był responsywny, jak widać na tym bardzo prostym przykładzie:

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

Możesz dostosować zawartość o różnych rozmiarach, zmieniając maxCrossAxisExtent .

Ten przykład służył głównie do pokazania istnienia konstruktora GridView.extent GridView , ale o wiele mądrzejszym sposobem na to byłoby użycie GridView.builder z SliverGridDelegateWithMaxCrossAxisExtent , w tym przypadku, gdy widżety mają być wyświetlane w siatce są tworzone dynamicznie z innej struktury danych, jak widać na tym przykładzie:

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

Przykładem adaptacji GridView do różnych ekranów jest moja osobista strona docelowa, która jest bardzo prostą aplikacją webową Flutter składającą się z GridView z mnóstwem Cards , tak jak poprzedni przykładowy kod, z tą różnicą, że Cards są trochę bardziej złożone i większe .

Bardzo prostą zmianą, którą można by wprowadzić w aplikacjach przeznaczonych na telefony, byłoby zastąpienie Drawer stałym menu po lewej stronie, gdy jest miejsce.

Na przykład, moglibyśmy mieć ListView widżetów, taki jak ten, który jest używany do nawigacji:

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

Na smartfonie, wspólne miejsce do użycia, które znajduje się w Drawer (znane również jako menu hamburgerowe).

Alternatywami do tego byłyby BottomNavigationBar lub TabBar w połączeniu z TabBarView , ale w przypadku obu musielibyśmy wprowadzić więcej zmian niż jest to wymagane w szufladzie, więc będziemy trzymać się szuflady.

Aby wyświetlić tylko Drawer zawierającą Menu , które widzieliśmy wcześniej na mniejszych ekranach, napisz kod, który wygląda jak poniższy fragment, sprawdzając szerokość za pomocą MediaQuery.of(context) i przekazując obiekt Drawer do Scaffold tylko wtedy, gdy jest mniejsza niż pewna wartość szerokości, która naszym zdaniem jest odpowiednia dla naszej aplikacji:

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

Zastanówmy się teraz nad body Scaffold . Jako przykładową główną zawartość naszej aplikacji użyjemy GridView , który zbudowaliśmy wcześniej, który przechowujemy w oddzielnym widgecie o nazwie Content , aby uniknąć nieporozumień:

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

Na większych ekranach samo ciało może być Row , który pokazuje dwa widżety: Menu , którego szerokość jest ograniczona, oraz Content wypełniająca resztę ekranu.

Na mniejszych ekranach całe body będzie Content .

Zapakujemy wszystko w SafeArea i Center , ponieważ czasami widżety aplikacji internetowych Flutter, zwłaszcza w przypadku korzystania z Row s i Column , kończą poza widocznym obszarem ekranu, co jest naprawione za pomocą SafeArea i/lub Center .

Oznacza to, że body Scaffold będzie następujący:

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

Oto wszystko razem:

(duży podgląd)
 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]) ) ) ) ); }

To jest większość rzeczy, których będziesz potrzebować jako ogólne wprowadzenie do responsywnego interfejsu użytkownika we Flutterze. Wiele z jego aplikacji będzie zależeć od konkretnego interfejsu użytkownika aplikacji i trudno jest dokładnie określić, co możesz zrobić, aby Twoja aplikacja była responsywna, i możesz zastosować wiele podejść w zależności od swoich preferencji. Teraz jednak zobaczmy, jak możemy przekształcić bardziej kompletny przykład w responsywną aplikację, myśląc o typowych elementach aplikacji i przepływach interfejsu użytkownika.

Umieszczanie tego w kontekście: tworzenie responsywnej aplikacji

Na razie mamy tylko ekran. Rozwińmy to do dwuekranowej aplikacji z działającą nawigacją opartą na adresach URL!

Tworzenie responsywnej strony logowania

Możliwe, że Twoja aplikacja ma stronę logowania. Jak możemy sprawić, by było to responsywne?

Ekrany logowania na urządzeniach mobilnych są zazwyczaj dość do siebie podobne. Dostępna przestrzeń jest niewielka; zwykle jest to po prostu Column z Padding wokół swoich widżetów i zawiera TextField do wpisywania nazwy użytkownika i hasła oraz przycisk do logowania. Więc jest to dość standardowe (choć nie działa, ponieważ wymagałoby to między innymi , TextEditingController dla każdej strony logowania TextField ) dla aplikacji mobilnej może wyglądać następująco:

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

Wygląda dobrze na urządzeniu mobilnym, ale te bardzo szerokie TextField zaczynają wyglądać irytująco na tablecie, nie mówiąc już o większym ekranie. Jednak nie możemy po prostu zdecydować się na stałą szerokość, ponieważ telefony mają różne rozmiary ekranu i powinniśmy zachować pewien stopień elastyczności.

Na przykład, eksperymentując, możemy stwierdzić, że maksymalna szerokość powinna wynosić 500. Cóż, ustawilibyśmy constraints Container na 500 (w poprzednim przykładzie użyłem Container zamiast Padding , ponieważ wiedziałem, dokąd zmierzam z tym ) i możemy już iść, prawda? Niezupełnie, ponieważ spowodowałoby to, że widżety logowania trzymałyby się po lewej stronie ekranu, co może być nawet gorsze niż rozciąganie wszystkiego. Zawijamy więc widżet Center , taki jak ten:

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

To już wygląda dobrze, a nie musieliśmy nawet używać LayoutBuilder ani MediaQuery.of(context).size . Pójdźmy jednak o krok dalej, aby wyglądało to bardzo dobrze. Moim zdaniem wyglądałoby to lepiej, gdyby pierwsza część była w jakiś sposób oddzielona od tła. Możemy to osiągnąć, nadając kolor tła temu, co znajduje się za Container za pomocą widżetów wejściowych, i zachowując biały Container pierwszego planu. Aby wyglądało to trochę lepiej, zachowajmy Container przed rozciąganiem się do góry i dołu ekranu na dużych urządzeniach, zaokrąglijmy rogi i dajmy mu ładne animowane przejście między dwoma układami.

Wszystko to wymaga teraz LayoutBuilder i zewnętrznego Container , aby zarówno ustawić kolor tła, jak i dodać wypełnienie wokół Container , a nie tylko po bokach, tylko na większych ekranach. Ponadto, aby zmienić ilość animowanego wypełnienia, wystarczy zmienić ten zewnętrzny Container w AnimatedContainer , co wymaga duration animacji, który ustawimy na pół sekundy, czyli Duration(milliseconds: 500) w kod.

Oto przykład responsywnej strony logowania:

(duży podgląd)
 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() ) ); } ) ] ), ), ) ); } ) ); }

Jak widać, zmieniłem również RaisedButton onPressed na wywołanie zwrotne, które kieruje nas do ekranu o nazwie HomePage (którym może być na przykład widok, który zbudowaliśmy wcześniej za pomocą GridView i menu lub szuflady). Teraz jednak skupimy się na tej części nawigacji.

Nazwane trasy: nawigacja w aplikacji bardziej jak właściwa aplikacja internetowa

Typową cechą aplikacji internetowych jest możliwość zmiany ekranów na podstawie adresu URL. Na przykład przejście do https://appurl/login powinno dać ci coś innego niż https://appurl/somethingelse . W rzeczywistości Flutter obsługuje nazwane trasy , które mają dwa cele:

  1. W aplikacji internetowej mają dokładnie tę funkcję, o której wspomniałem w poprzednim zdaniu.
  2. W dowolnej aplikacji pozwalają one wstępnie zdefiniować trasy dla aplikacji i nadawać im nazwy, a następnie nawigować do nich, po prostu określając ich nazwę.

Aby to zrobić, musimy zmienić konstruktor MaterialApp na taki, który wygląda następująco:

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

A następnie możemy przełączyć się na inną trasę, używając Navigator.pushNamed(context, routeName) i Navigator.pushReplacementNamed(context, routeName) , zamiast Navigator.push(context, route) i Navigator.pushReplacement(context, route) .

Oto to, co dotyczy hipotetycznej aplikacji, którą zbudowaliśmy w dalszej części tego artykułu. Tak naprawdę nie możesz zobaczyć nazwanych tras w akcji w DartPad, więc powinieneś wypróbować to na własnej maszynie za pomocą flutter run lub sprawdzić przykład w akcji:

(duży podgląd)
 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]) ) ) ) ); }

Naprzód ze swoją przygodą we trzepotaniu

To powinno dać ci wyobrażenie o tym, co możesz zrobić z Flutterem na większych ekranach, szczególnie w Internecie. Jest to urocza platforma, bardzo łatwa w użyciu, a jej ekstremalna obsługa wielu platform sprawia, że ​​nauka i rozpoczęcie korzystania z niej jest bardziej istotne. Więc śmiało zacznij ufać Flutterowi również w zakresie aplikacji internetowych!

Dalsze zasoby

  • „Powłoki pulpitu”, GitHub
    Aktualny, zawsze aktualny stan Fluttera na pulpicie
  • „Wsparcie dla komputerów stacjonarnych dla Fluttera”, Flutter
    Informacje o w pełni obsługiwanych platformach stacjonarnych
  • „Wsparcie internetowe dla Flutter”, Flutter
    Informacje o Flutterze w sieci
  • „Wszystkie próbki”, Flutter
    Wyselekcjonowana lista próbek i aplikacji Flutter