Pengembangan Web dan Desktop yang Responsif Dengan Flutter
Diterbitkan: 2022-03-10Tutorial ini bukan pengenalan Flutter itu sendiri. Ada banyak artikel, video, dan beberapa buku yang tersedia secara online dengan perkenalan sederhana yang akan membantu Anda mempelajari dasar-dasar Flutter. Sebagai gantinya, kami akan membahas dua tujuan berikut:
- Status pengembangan non-seluler Flutter saat ini dan bagaimana Anda dapat menjalankan kode Flutter di browser, di komputer desktop atau laptop;
- Cara membuat aplikasi responsif menggunakan Flutter, sehingga Anda dapat melihat kekuatannya — terutama sebagai kerangka kerja web — pada tampilan penuh, diakhiri dengan catatan tentang perutean berdasarkan URL.
Mari kita masuk ke dalamnya!
Apa Itu Flutter, Mengapa Itu Penting, Apa yang Telah Berkembang Menjadi, Ke Mana Arahnya
Flutter adalah kerangka kerja pengembangan aplikasi terbaru Google. Google membayangkannya untuk mencakup semua: Ini akan memungkinkan kode yang sama untuk dieksekusi pada smartphone dari semua merek, pada tablet, dan pada komputer desktop dan laptop sebagai aplikasi asli atau sebagai halaman web.
Ini adalah proyek yang sangat ambisius, tetapi Google telah sangat sukses sampai sekarang terutama dalam dua aspek: dalam menciptakan kerangka kerja yang benar-benar platform-independen untuk aplikasi asli Android dan iOS yang bekerja dengan baik dan sepenuhnya siap untuk penggunaan produksi, dan dalam menciptakan front yang mengesankan. -end web framework yang dapat membagikan 100% kode dengan aplikasi Flutter yang kompatibel.
Di bagian selanjutnya, kita akan melihat apa yang membuat aplikasi ini kompatibel dan bagaimana perkembangan Flutter non-seluler saat ini.
Pengembangan Non-Seluler Dengan Flutter
Pengembangan non-seluler dengan Flutter pertama kali dipublikasikan secara signifikan di Google I/O 2019. Bagian ini adalah tentang cara membuatnya berfungsi dan tentang waktu kerjanya.
Cara Mengaktifkan Pengembangan Web Dan Desktop
Untuk mengaktifkan pengembangan web, Anda harus berada di saluran beta Flutter terlebih dahulu. Ada dua cara untuk sampai ke titik itu:
- Instal Flutter langsung di saluran beta dengan mengunduh versi beta terbaru yang sesuai dari arsip SDK.
- Jika Anda sudah menginstal Flutter, alihkan ke saluran beta dengan
$ flutter channel beta
, lalu lakukan peralihan itu sendiri dengan memperbarui versi Flutter Anda (yang sebenarnya adalahgit pull
pada folder instalasi Flutter) dengan$ flutter upgrade
.
Setelah itu, Anda dapat menjalankan ini:
$ flutter config --enable-web
Dukungan desktop jauh lebih eksperimental, terutama karena kurangnya alat untuk Linux dan Windows, membuat pengembangan plugin sangat merepotkan, dan karena fakta bahwa API yang digunakan ditujukan untuk penggunaan proof-of-concept dan bukan untuk produksi. Ini tidak seperti pengembangan web, yang menggunakan kompiler dart2js yang telah dicoba dan diuji untuk build rilis, yang bahkan tidak didukung untuk aplikasi desktop asli Windows dan Linux.
Catatan : Dukungan untuk macOS sedikit lebih baik daripada dukungan untuk Windows dan Linux, tetapi masih tidak sebaik dukungan untuk web dan tidak sebagus dukungan penuh untuk platform seluler.
Untuk mengaktifkan dukungan untuk pengembangan desktop, Anda perlu beralih ke saluran rilis master
dengan mengikuti langkah-langkah yang sama yang diuraikan sebelumnya untuk saluran beta
. Kemudian, jalankan perintah berikut dengan mengganti <OS_NAME>
dengan linux
, windows
, atau macos
:
$ flutter config --enable-<OS_NAME>-desktop
Pada titik ini, jika Anda memiliki masalah dengan salah satu langkah berikut yang akan saya jelaskan karena alat Flutter tidak melakukan apa yang saya katakan, beberapa langkah pemecahan masalah yang umum adalah sebagai berikut:
- Jalankan
flutter doctor
untuk memeriksa masalah. Efek samping dari perintah Flutter ini adalah ia harus mengunduh alat apa pun yang diperlukan yang tidak dimilikinya. - Jalankan
flutter upgrade
. - Matikan dan hidupkan lagi. Jawaban dukungan teknis tingkat-1 yang lama untuk memulai ulang komputer Anda mungkin tepat seperti yang Anda perlukan untuk dapat menikmati kekayaan Flutter sepenuhnya.
Menjalankan Dan Membangun Aplikasi Web Flutter
Dukungan web Flutter tidak buruk sama sekali, dan ini tercermin dalam kemudahan pengembangan untuk web.
Menjalankan ini…
$ flutter devices
… harus segera menunjukkan entri untuk sesuatu seperti ini:
Web Server • web-server • web-javascript • Flutter Tools
Selain itu, menjalankan browser Chrome juga akan menyebabkan Flutter menampilkan entri untuknya. Menjalankan flutter run
pada proyek Flutter yang kompatibel (lebih lanjut tentang itu nanti) ketika satu-satunya "perangkat yang terhubung" yang muncul adalah server web akan menyebabkan Flutter memulai server web di localhost:<RANDOM_PORT>
, yang memungkinkan Anda mengakses Flutter Anda aplikasi web dari browser apa pun.
Jika Anda telah menginstal Chrome tetapi tidak muncul, Anda perlu menyetel variabel lingkungan CHROME_EXECUTABLE
ke jalur ke file yang dapat dieksekusi Chrome.
Menjalankan Dan Membangun Aplikasi Desktop Flutter
Setelah Anda mengaktifkan dukungan desktop Flutter, Anda dapat menjalankan aplikasi Flutter secara native di workstation pengembangan Anda dengan flutter run -d <OS_NAME>
, menggantikan <OS_NAME>
dengan nilai yang sama yang Anda gunakan saat mengaktifkan dukungan desktop. Anda juga dapat membangun binari di direktori build
dengan flutter build <OS_NAME>
.
Namun, sebelum Anda dapat melakukan semua itu, Anda harus memiliki direktori yang berisi apa yang perlu dibuat Flutter untuk platform Anda. Ini akan dibuat secara otomatis saat Anda membuat proyek baru, tetapi Anda harus membuatnya untuk proyek yang sudah ada dengan flutter create .
. Selain itu, API Linux dan Windows tidak stabil, jadi Anda mungkin harus membuat ulang untuk platform tersebut jika aplikasi berhenti bekerja setelah pembaruan Flutter.
Kapan Aplikasi Kompatibel?
Apa yang saya maksud selama ini ketika menyebutkan bahwa aplikasi Flutter harus menjadi "proyek yang kompatibel" agar dapat berfungsi di desktop atau web? Sederhananya, maksud saya itu tidak boleh menggunakan plugin apa pun yang tidak memiliki implementasi khusus platform untuk platform tempat Anda mencoba membangun.
Untuk memperjelas poin ini bagi semua orang dan menghindari kesalahpahaman, harap perhatikan bahwa plugin Flutter adalah paket Flutter tertentu yang berisi kode khusus platform yang diperlukan untuk menyediakan fitur-fiturnya.
Misalnya, Anda dapat menggunakan paket url_launcher
yang dikembangkan Google sebanyak yang Anda inginkan (dan Anda mungkin ingin, mengingat web dibangun di atas hyperlink).
Contoh paket yang dikembangkan Google yang penggunaannya akan menghalangi pengembangan web adalah path_provider
, yang digunakan untuk mendapatkan jalur penyimpanan lokal untuk menyimpan file. Ini adalah contoh paket yang, kebetulan, tidak berguna untuk aplikasi web, jadi tidak dapat menggunakannya bukanlah hal yang mengecewakan, kecuali kenyataan bahwa Anda perlu mengubah kode Anda untuk untuk bekerja di web jika Anda menggunakannya.
Misalnya, Anda dapat menggunakan paket shared_preferences, yang bergantung pada localStorage
HTML di web.
Peringatan serupa berlaku mengenai platform desktop: Sangat sedikit plugin yang kompatibel dengan platform desktop, dan, karena ini adalah tema yang berulang, lebih banyak pekerjaan yang perlu dilakukan di sisi desktop daripada yang benar-benar diperlukan di Flutter untuk web.
Membuat Layout Responsif Di Flutter
Karena apa yang saya jelaskan di atas dan untuk kesederhanaan, saya akan berasumsi untuk sisa posting ini bahwa platform target Anda adalah web, tetapi konsep dasarnya juga berlaku untuk pengembangan desktop.
Mendukung web memiliki manfaat dan tanggung jawab. Menjadi cukup banyak dipaksa untuk mendukung ukuran layar yang berbeda mungkin terdengar seperti kelemahan, tetapi pertimbangkan bahwa menjalankan aplikasi di browser web memungkinkan Anda untuk melihat dengan sangat mudah bagaimana aplikasi Anda akan terlihat di layar dengan ukuran dan rasio aspek yang berbeda, tanpa harus menjalankannya secara terpisah. emulator perangkat seluler.
Sekarang, mari kita bicara kode. Bagaimana Anda bisa membuat aplikasi Anda responsif?
Ada dua perspektif dari mana analisis ini dilakukan:
- “Widget apa yang saya gunakan atau dapatkah saya gunakan yang dapat atau harus beradaptasi dengan layar dengan ukuran berbeda?”
- “Bagaimana saya bisa mendapatkan informasi tentang ukuran layar, dan bagaimana saya bisa menggunakannya saat menulis kode UI?”
Kami akan menjawab pertanyaan pertama nanti. Pertama-tama mari kita bicara tentang yang terakhir, karena itu dapat ditangani dengan sangat mudah dan merupakan inti dari masalah ini. Ada dua cara untuk melakukannya:
- Salah satu caranya adalah dengan mengambil informasi dari
MediaQueryData
dariMediaQuery
rootInheritedWidget
, yang harus ada di pohon widget agar aplikasi Flutter berfungsi (itu bagian dariMaterialApp/WidgetsApp/CupertinoApp
), yang bisa Anda dapatkan, sepertiInheritedWidget
lainnya, denganMediaQuery.of(context)
, yang memiliki propertisize
, yang bertipeSize
, dan karenanya memiliki dua propertiwidth
danheight
bertipedouble
. - Cara lainnya adalah dengan menggunakan
LayoutBuilder
, yang merupakan widget builder (sepertiStreamBuilder
atauFutureBuilder
) yang meneruskan ke fungsibuilder
(bersama dengancontext
) objekBoxConstraints
yang memilikiminHeight
,maxHeight
,minWidth
danmaxWidth
.
Berikut adalah contoh DartPad menggunakan MediaQuery
untuk mendapatkan batasan, kodenya adalah sebagai berikut:
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 ) ] ) ) ); }
Dan inilah yang menggunakan LayoutBuilder
untuk hal yang sama:
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 ) ] ) ) ) ); }
Sekarang, mari kita pikirkan tentang widget apa yang dapat beradaptasi dengan batasan.
Pertama-tama, mari kita pikirkan tentang berbagai cara meletakkan beberapa widget sesuai dengan ukuran layar.
Widget yang paling mudah beradaptasi adalah GridView
. Faktanya, GridView
yang dibuat menggunakan konstruktor GridView.extent
bahkan tidak memerlukan keterlibatan Anda untuk dibuat responsif, seperti yang dapat Anda lihat dalam contoh yang sangat sederhana ini:
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() ) ); }
Anda dapat mengakomodasi konten dengan ukuran berbeda dengan mengubah maxCrossAxisExtent
.
Contoh tersebut sebagian besar bertujuan untuk menunjukkan keberadaan konstruktor GridView.extent
GridView
, tetapi cara yang lebih cerdas untuk melakukannya adalah dengan menggunakan GridView.builder
dengan SliverGridDelegateWithMaxCrossAxisExtent
, dalam hal ini di mana widget akan ditampilkan di grid dibuat secara dinamis dari struktur data lain, seperti yang Anda lihat dalam contoh ini:
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]) ) ) ) ) ); }
Contoh adaptasi GridView ke layar yang berbeda adalah halaman arahan pribadi saya, yang merupakan aplikasi web Flutter yang sangat sederhana yang terdiri dari GridView
dengan sekumpulan Cards
, sama seperti kode contoh sebelumnya, kecuali bahwa Cards
sedikit lebih kompleks dan lebih besar .
Perubahan yang sangat sederhana yang dapat dilakukan pada aplikasi yang dirancang untuk ponsel adalah mengganti Drawer
dengan menu permanen di sebelah kiri bila ada ruang.
Misalnya, kita dapat memiliki ListView
widget, seperti berikut ini, yang digunakan untuk navigasi:
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"), ) ) ] ); }
Pada smartphone, tempat umum untuk digunakan adalah di dalam Drawer
(juga dikenal sebagai menu hamburger).
Alternatif untuk itu adalah BottomNavigationBar
atau TabBar
, dalam kombinasi dengan TabBarView
, tetapi dengan keduanya kita harus membuat lebih banyak perubahan daripada yang diperlukan dengan laci, jadi kita akan tetap menggunakan laci.
Untuk hanya menampilkan Drawer
yang berisi Menu
yang kita lihat sebelumnya di layar yang lebih kecil, Anda akan menulis kode yang terlihat seperti cuplikan berikut, memeriksa lebarnya menggunakan MediaQuery.of(context)
dan meneruskan objek Drawer
ke Scaffold
hanya jika itu kurang dari beberapa nilai lebar yang kami yakini sesuai untuk aplikasi kami:
Scaffold( appBar: AppBar(/* ... \*/), drawer: MediaQuery.of(context).size.width < 500 ? Drawer( child: Menu(), ) : null, body: /* ... \*/ )
Sekarang, mari kita pikirkan tentang body
Scaffold
. Sebagai contoh konten utama aplikasi kami, kami akan menggunakan GridView
yang kami buat sebelumnya, yang kami simpan di widget terpisah bernama Content
untuk menghindari kebingungan:
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]) ) ) ) ); }
Pada layar yang lebih besar, isi itu sendiri mungkin berupa Row
yang menampilkan dua widget: Menu
, yang dibatasi untuk lebar tetap, dan Content
yang mengisi seluruh layar.
Pada layar yang lebih kecil, seluruh body
akan menjadi Content
.
Kami akan membungkus semuanya dalam SafeArea
dan Center
karena terkadang widget aplikasi web Flutter, terutama saat menggunakan Row
s dan Column
s, berakhir di luar area layar yang terlihat, dan itu diperbaiki dengan SafeArea
dan/atau Center
.
Ini berarti body
Scaffold
akan menjadi sebagai berikut:
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() ) ] ) ) )
Inilah semua yang disatukan:
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]) ) ) ) ); }
Ini adalah sebagian besar hal yang Anda perlukan sebagai pengantar umum untuk UI responsif di Flutter. Sebagian besar aplikasinya akan bergantung pada UI spesifik aplikasi Anda, dan sulit untuk menentukan dengan tepat apa yang dapat Anda lakukan untuk membuat aplikasi Anda responsif, dan Anda dapat mengambil banyak pendekatan tergantung pada preferensi Anda. Sekarang, mari kita lihat bagaimana kita dapat membuat contoh yang lebih lengkap menjadi aplikasi responsif, dengan memikirkan elemen aplikasi umum dan alur UI.
Menempatkannya Dalam Konteks: Membuat Aplikasi Responsif
Sejauh ini, kami hanya memiliki layar. Mari kita kembangkan itu menjadi aplikasi dua layar dengan navigasi berbasis URL yang berfungsi!
Membuat Halaman Login yang Responsif
Kemungkinan aplikasi Anda memiliki halaman login. Bagaimana kita bisa membuatnya responsif?
Layar masuk pada perangkat seluler biasanya sangat mirip satu sama lain. Ruang yang tersedia tidak banyak; itu biasanya hanya Column
dengan beberapa Padding
di sekitar widgetnya, dan berisi TextField
s untuk mengetikkan nama pengguna dan kata sandi dan tombol untuk masuk. Jadi, cukup standar (meskipun tidak berfungsi, seperti yang diperlukan, antara lain , halaman login TextEditingController
untuk setiap TextField
) untuk aplikasi seluler dapat berupa sebagai berikut:
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: () {} ) ] ), ), )
Kelihatannya bagus di perangkat seluler, tetapi TextField
yang sangat lebar itu mulai terlihat menggelegar di tablet, apalagi layar yang lebih besar. Namun, kami tidak bisa hanya memutuskan lebar tetap karena ponsel memiliki ukuran layar yang berbeda, dan kami harus mempertahankan tingkat fleksibilitas.
Misalnya, melalui eksperimen, kita mungkin menemukan bahwa lebar maksimum harus 500. Nah, kita akan mengatur constraints
Container
menjadi 500 (Saya menggunakan Container
daripada Padding
pada contoh sebelumnya karena saya tahu kemana saya akan pergi dengan ini ) dan kami baik-baik saja, bukan? Tidak juga, karena itu akan menyebabkan widget login menempel di sisi kiri layar, yang mungkin lebih buruk daripada meregangkan semuanya. Jadi, kita bungkus dalam widget Center
, seperti ini:
Center( child: Container( constraints: BoxConstraints(maxWidth: 500), padding: const EdgeInsets.symmetric( vertical: 30.0, horizontal: 25.0 ), child: Column(/* ... \*/) ) )
Itu sudah terlihat bagus, dan kami bahkan tidak perlu menggunakan LayoutBuilder
atau MediaQuery.of(context).size
. Mari kita melangkah lebih jauh untuk membuat ini terlihat sangat bagus. Akan terlihat lebih baik, dalam pandangan saya, jika bagian latar depan dipisahkan dari latar belakang. Kita dapat mencapainya dengan memberikan warna latar belakang pada apa yang ada di belakang Container
dengan widget input, dan menjaga agar Container
latar depan tetap putih. Untuk membuatnya terlihat sedikit lebih baik, mari jaga agar Container
tidak meregang ke atas dan bawah layar pada perangkat besar, berikan sudut membulat, dan berikan transisi animasi yang bagus antara dua tata letak.
Semua itu sekarang membutuhkan LayoutBuilder
dan Container
luar untuk mengatur warna latar belakang dan menambahkan bantalan di sekitar Container
dan tidak hanya di sisi hanya pada layar yang lebih besar. Juga, untuk membuat perubahan dalam jumlah padding dianimasikan, kita hanya perlu mengubah Container
luar itu menjadi AnimatedContainer
, yang membutuhkan duration
untuk animasi, yang akan kita atur menjadi setengah detik, yaitu Duration(milliseconds: 500)
in kode.
Berikut contoh halaman login responsif:
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() ) ); } ) ] ), ), ) ); } ) ); }
Seperti yang Anda lihat, saya juga telah mengubah RaisedButton
's onPressed
menjadi callback yang mengarahkan kita ke layar bernama HomePage
(yang bisa berupa, misalnya, tampilan yang kita buat sebelumnya dengan GridView
dan menu atau laci). Sekarang, bagian navigasi itulah yang akan kita fokuskan.
Rute Bernama: Membuat Navigasi Aplikasi Anda Lebih Seperti Aplikasi Web yang Tepat
Hal umum yang dimiliki aplikasi web adalah kemampuan untuk mengubah layar berdasarkan URL. Misalnya pergi ke https://appurl/login
akan memberi Anda sesuatu yang berbeda dari https://appurl/somethingelse
. Flutter, pada kenyataannya, mendukung rute bernama , yang memiliki dua tujuan:
- Di aplikasi web, mereka memiliki fitur yang persis seperti yang saya sebutkan di kalimat sebelumnya.
- Di aplikasi apa pun, mereka memungkinkan Anda untuk menentukan rute untuk aplikasi Anda dan memberi mereka nama, dan kemudian dapat menavigasi ke mereka hanya dengan menentukan nama mereka.
Untuk melakukannya, kita perlu mengubah konstruktor MaterialApp
menjadi seperti berikut:
MaterialApp( initialRoute: "/login", routes: { "/login": (context) => LoginPage(), "/home": (context) => HomePage() } );
Dan kemudian kita dapat beralih ke rute yang berbeda dengan menggunakan Navigator.pushNamed(context, routeName)
dan Navigator.pushReplacementNamed(context, routeName)
, alih-alih Navigator.push(context, route)
dan Navigator.pushReplacement(context, route)
.
Inilah yang diterapkan pada aplikasi hipotetis yang kami buat di sisa artikel ini. Anda tidak dapat benar-benar melihat rute bernama beraksi di DartPad, jadi Anda harus mencobanya di mesin Anda sendiri dengan flutter run
, atau periksa contoh dalam tindakan:
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]) ) ) ) ); }
Maju Dengan Petualangan Flutter Anda
Itu akan memberi Anda gambaran tentang apa yang dapat Anda lakukan dengan Flutter di layar yang lebih besar, khususnya di web. Ini adalah kerangka kerja yang indah, sangat mudah digunakan, dan dukungan lintas platform yang ekstrim hanya membuatnya lebih penting untuk dipelajari dan mulai digunakan. Jadi, lanjutkan dan mulai memercayai Flutter untuk aplikasi web juga!
Sumber Daya Lebih Lanjut
- "Kerang desktop", GitHub
Status Flutter saat ini dan selalu terbaru di desktop - “Dukungan desktop untuk Flutter”, Flutter
Informasi tentang platform desktop yang didukung penuh - “Dukungan web untuk Flutter”, Flutter
Informasi tentang Flutter untuk web - “Semua Sampel”, Flutter
Daftar sampel dan aplikasi Flutter yang dikuratori