Pengembangan Web dan Desktop yang Responsif Dengan Flutter

Diterbitkan: 2022-03-10
Ringkasan cepat Flutter telah membuat gebrakan di kancah pengembangan seluler. Sekarang menggunakan perangkat yang lebih besar juga. Inilah yang perlu Anda ketahui agar siap mengemban tugas mengembangkan aplikasi web dan desktop menggunakan kerangka kerja lintas platform yang luar biasa ini.

Tutorial 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:

  1. Status pengembangan non-seluler Flutter saat ini dan bagaimana Anda dapat menjalankan kode Flutter di browser, di komputer desktop atau laptop;
  2. 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.

Lebih banyak setelah melompat! Lanjutkan membaca di bawah 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 adalah git 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:

  1. “Widget apa yang saya gunakan atau dapatkah saya gunakan yang dapat atau harus beradaptasi dengan layar dengan ukuran berbeda?”
  2. “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:

  1. Salah satu caranya adalah dengan mengambil informasi dari MediaQueryData dari MediaQuery root InheritedWidget , yang harus ada di pohon widget agar aplikasi Flutter berfungsi (itu bagian dari MaterialApp/WidgetsApp/CupertinoApp ), yang bisa Anda dapatkan, seperti InheritedWidget lainnya, dengan MediaQuery.of(context) , yang memiliki properti size , yang bertipe Size , dan karenanya memiliki dua properti width dan height bertipe double .
  2. Cara lainnya adalah dengan menggunakan LayoutBuilder , yang merupakan widget builder (seperti StreamBuilder atau FutureBuilder ) yang meneruskan ke fungsi builder (bersama dengan context ) objek BoxConstraints yang memiliki minHeight , maxHeight , minWidth dan maxWidth .

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:

(Pratinjau besar)
 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:

(Pratinjau besar)
 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:

  1. Di aplikasi web, mereka memiliki fitur yang persis seperti yang saya sebutkan di kalimat sebelumnya.
  2. 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:

(Pratinjau besar)
 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