Bagaimana saya melakukan aplikasi desktop di Flutter (+ bonus)

Baru-baru ini, berita itu menarik perhatian saya bahwa rilis Flutter (1.9) berikutnya dirilis , yang menjanjikan berbagai barang, termasuk dukungan awal untuk aplikasi web.

Di tempat kerja, saya sedang mengembangkan aplikasi seluler di React Native, tetapi saya melihat Flutter dengan rasa ingin tahu. Bagi mereka yang tidak tahu: di Flutter Anda sekarang dapat membuat aplikasi untuk Android dan iOS, dukungan untuk aplikasi web sedang disiapkan untuk rilis, dan ada juga rencana untuk mendukung desktop.

Tersebut adalah "satu cincin untuk menguasai semua."

Setelah mengalihkan pikiran saya selama beberapa hari tentang aplikasi seperti apa yang dapat Anda coba buat, saya memutuskan untuk memilih tugas dengan tanda bintang - apa yang kita perlukan trek yang sudah usang ini? Ayun pada desktop dan gagah mengatasi kesulitan! Ke depan, saya akan mengatakan bahwa hampir tidak ada kesulitan muncul.

Di bawah potongan - sebuah cerita tentang bagaimana saya menyelesaikan tugas programmer React Native yang biasa menggunakan alat Flutter, ditambah kesan umum dari teknologi.



Memikirkan fitur Flutter yang ingin saya "sentuh", saya memutuskan bahwa dalam aplikasi saya seharusnya:

  • permintaan ke API jarak jauh;
  • transisi antar layar;
  • Animasi transisi
  • manajer negara - redux atau yang serupa.

Saya tidak tahu cara backend, jadi saya memutuskan untuk mencari API terbuka pihak ketiga. Akibatnya, saya menetapkan sumber ini - kursus CBR dalam XML dan JSON, API . Nah, di sini saya akhirnya memutuskan fungsionalitas aplikasi: akan ada dua layar, yang utama ada daftar mata uang pada tingkat CBR, ketika Anda mengklik pada item daftar kami membuka layar dengan informasi rinci.

Persiapan


Karena perintah flutter create belum tahu cara membuat proyek untuk Windows / Linux (hanya Mac saat ini didukung, gunakan flag --macos untuk ini), Anda harus menggunakan repositori ini , di mana ada contoh yang disiapkan. Kami mengkloning repositori, mengambil folder example dari sana, jika perlu, ganti nama dan terus bekerja di dalamnya.

Karena dukungan untuk platform desktop masih dalam pengembangan, Anda masih perlu melakukan sejumlah manipulasi. Untuk mengakses fitur yang sedang dikembangkan, jalankan di terminal:

 flutter channel master flutter upgrade 

Selain itu, Anda perlu memberi tahu Flutter bahwa itu dapat menggunakan platform Anda:

 flutter config --enable-linux-desktop 

atau

 flutter config --enable-macos-desktop 

atau

 flutter config --enable-windows-desktop 

Jika semuanya berjalan dengan baik, maka dengan menjalankan perintah flutter doctor Anda akan melihat output yang sama:



Jadi, pemandangan sudah siap, para penonton di aula - kita bisa mulai.

Tata letak


Hal pertama yang menarik perhatian Anda setelah React Native adalah tidak adanya bahasa markup khusus ala JSX. Flutter memaksa Anda untuk menulis markup dan logika bisnis di Dart . Pada awalnya, ini menjengkelkan: tampilan tidak ada hubungannya, kodenya tampak rumit, dan bahkan tanda kurung ini ada di akhir komponen!

Misalnya, seperti:



Dan ini bukan batasnya! Layak untuk menghapus satu di tempat yang salah dan hiburan (tidak) menyenangkan dijamin untuk Anda.

Selain itu, karena kekhasan komponen penataan di Flutter, untuk komponen besar, lekukan dari tepi kiri editor meningkat cukup cepat, dan dengan itu jumlah tanda kurung untuk ditutup.

Fitur ini adalah bahwa dalam gaya Flutter adalah komponen yang sama (lebih tepatnya - widget).

Jika dalam Bereaksi Asli untuk mengatur tiga tombol dalam satu baris di dalam View sehingga mereka secara merata mendistribusikan ruang kontainer, cukup bagi saya untuk menentukan flexDirection: 'row' untuk View in styles, dan menambahkan flex: 1 untuk tombol dalam style, kemudian Flutter memiliki komponen terpisah Row untuk mengatur elemen dalam satu baris dan yang terpisah untuk "perluasan" elemen di seluruh ruang yang tersedia: Expanded .

Akibatnya, bukannya

 <View style={{height: 100, width:300, flexDirection: 'row'}}> <Button title='A' style={{flex:1}}> <Button title='B' style={{flex:1}}> <Button title='C' style={{flex:1}}> </View> 

kita harus menulis seperti ini:

 Container( height: 100, width: 300, child: Row( children: <Widget>[ Expanded( child: RaisedButton( onPressed: () {}, child: Text('A'), ), ), Expanded( child: RaisedButton( onPressed: () {}, child: Text('B'), ), ), Expanded( child: RaisedButton( onPressed: () {}, child: Text('C'), ), ), ], ), ) 

Lebih banyak kata-kata, bukan?

Atau, katakanlah, Anda ingin menambahkan bingkai dengan ujung bulat ke wadah ini. Di Bereaksi Asli, kami cukup menambahkan gaya:

 borderRadius: 5, borderWidth: 1, borderColor: '#ccc' 

Di Flutter, kita harus menambahkan sesuatu seperti ini ke argumen kontainer:

 decoration: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(5)), border: Border.all(width: 1, color: Color(0xffcccccc)) ), 

Secara umum, pada awalnya, markup saya berubah menjadi lembaran kode besar, di mana iblis akan mematahkan kakinya. Namun, tidak semuanya buruk.

Pertama, komponen besar tentu saja harus diuraikan - ditempatkan di widget yang terpisah atau setidaknya dalam metode kelas widget Anda.

Kedua, plugin Flutter dalam VS Code sangat membantu - dalam gambar di atas komentar untuk tanda kurung ditandatangani oleh plugin itu sendiri (dan mereka tidak terhapus), yang membantu untuk tidak bingung dalam tanda kurung. Ditambah alat pemformatan otomatis - setelah setengah jam Anda terbiasa menekan Ctrl+Shift+I secara berkala untuk memformat kode.

Selain itu, sintaksis bahasa Dart dalam edisi kedua menjadi jauh lebih menyenangkan, sehingga pada akhir hari saya sudah menikmati menggunakannya. Tidak biasa Ya Tapi bukan tidak menyenangkan.

Permintaan API


Di Bereaksi Asli, untuk mendapatkan data dari beberapa API, kami biasanya menggunakan metode fetch , yang mengembalikan Promise kami.

Dalam Flutter, situasinya mirip. Setelah melihat contoh-contoh dalam dokumentasi, saya menambahkan paket http ke pubspec.yaml (analog dari package.json dari dunia JS) dan menulis sesuatu seperti ini:

 Future<http.Response> getAnything() { return http.get(URL); } 

Objek Future sangat mirip artinya dengan Promise, jadi semuanya cukup transparan di sini. Nah, untuk serialisasi / deserializing objek json, Anda dapat menggunakan konsep kelas model dengan metode khusus fromJSON / toJSON . Anda dapat membaca lebih lanjut tentang ini di dokumentasi .

Transisi antar layar


Terlepas dari kenyataan bahwa saya membuat aplikasi desktop, dari sudut pandang Flutter tidak ada perbedaan pada platform mana ia berputar. Nah, itu, dalam kasus saya ini begitu, secara umum - saya tidak tahu. Bahkan, jendela sistem tempat aplikasi bergetar diluncurkan adalah layar yang sama dari sebuah smartphone.

Transisi antar layar cukup sepele: kami membuat kelas widget layar, dan kemudian menggunakan kelas Navigator standar.

Dalam kasus paling sederhana, mungkin terlihat seperti ini:

 RaisedButton( child: Text('Go to Detail'), onPressed: () { Navigator.of(context).push<void>(MaterialPageRoute(builder: (context) => DetailScreen())); }, ) 

Jika aplikasi Anda memiliki beberapa layar, lebih masuk akal untuk terlebih dahulu menyiapkan kamus rute, dan kemudian menggunakan metode pushNamed . Contoh kecil dari dokumentasi:

 class NavigationApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( ... routes: <String, WidgetBuilder>{ '/a': (BuildContext context) => usualNavscreen(), '/b': (BuildContext context) => drawerNavscreen(), } ... ); } } // AnyWidget ... onPressed: () { Navigator.of(context).pushNamed('/a'); }, ... 

Selain itu, Anda dapat menyiapkan animasi khusus untuk beralih antar layar dan menulis sesuatu seperti ini:

 Navigator.of(context).push<void>(ScaleRoute(page: DetailScreen())); 

Di sini ScaleRoute adalah kelas khusus untuk membuat animasi transisi. Contoh animasi yang bagus dapat ditemukan di sini .

Manajemen negara


Kebetulan kami perlu memiliki akses ke beberapa data dari bagian mana pun dari aplikasi kami. Dalam Bereaksi Asli, redux sering digunakan (jika tidak paling sering) untuk tujuan ini.

Untuk Flutter, ada repositori yang memberikan contoh menggunakan berbagai arsitektur aplikasi - ada Redux, MVC, dan MVU, dan bahkan yang belum pernah saya dengar sebelumnya.

Setelah mencari-cari sedikit dalam contoh-contoh ini, saya memutuskan untuk berhenti pada Provider .

Secara umum, idenya cukup sederhana: kami membuat kelas khusus yang ChangeNotifier kelas ChangeNotifier , di mana kami akan menyimpan data kami, memperbaruinya menggunakan metode kelas ini, dan mengambilnya dari sana jika perlu. Lihat dokumentasi paket untuk lebih jelasnya.

Untuk melakukan ini, tambahkan paket provider ke pubspec.yaml dan siapkan kelas Penyedia. Dalam kasus saya, tampilannya seperti ini:

 import 'package:flutter/material.dart'; import 'package:rates_app/models/rate.dart'; class RateProvider extends ChangeNotifier { Rate currentrate; void setCurrentRate(Rate rate) { this.currentrate = rate; notifyListeners(); } } 

Here Rate adalah kelas model mata uang saya (dengan name bidang, code , value , dll.), currentrate adalah bidang di mana mata uang yang dipilih akan disimpan, dan setCurrentRate adalah metode di mana nilai currentrate .

Untuk melampirkan penyedia kami ke aplikasi, kami mengubah kode kelas aplikasi:

 @override Widget build(BuildContext context) { return ChangeNotifierProvider( builder: (context) => RateProvider(), //   child: MaterialApp( ... ), home: HomeScreen(), ), ); } 

Itu dia, sekarang jika kita ingin menyimpan mata uang yang dipilih, maka kita menulis sesuatu seperti ini:

 Provider.of<RateProvider>(context).setCurrentRate(rate); 

Dan jika kita ingin mendapatkan nilai yang tersimpan, maka ini:

 var rate = Provider.of<RateProvider>(context).currentrate; 

Semuanya cukup transparan dan tidak ada boilerplate (tidak seperti Redux). Tentu saja, mungkin untuk aplikasi yang lebih kompleks semuanya akan berubah tidak begitu lancar, tetapi untuk mereka yang seperti contoh saya, anggur murni.

Bangun aplikasi


Secara teori, perintah flutter build <platform> digunakan untuk membangun aplikasi. Dalam praktiknya, ketika saya menjalankan perintah flutter build linux , saya menerima pesan ini:



"Tidak sakit," pikir saya, saya ngeri dengan berat folder build - 287,5 MB - dan karena kesederhanaan jiwa saya, saya menghapus folder ini selamanya. Ternyata - sia-sia.

Setelah menghapus direktori build , proyek berhenti dimulai. Saya tidak dapat mengembalikannya, jadi saya menyalinnya dari contoh aslinya. Itu tidak membantu - kolektor bersumpah pada file yang hilang.

Setelah melakukan sedikit riset, ternyata di folder ini ada file snapshot_blob.bin.d , yang, tampaknya, berisi jalur ke semua file yang digunakan dalam proyek. Saya menambahkan jalur yang hilang dan berhasil.

Jadi, saat ini Flutter tidak tahu bagaimana mempersiapkan rilis build untuk desktop. Bagaimanapun, untuk Linux.

Secara umum, jika Anda menutup mata untuk minus ini, aplikasi ternyata seperti yang saya inginkan dan terlihat

jadi


Bonus


Kami memberikan bonus yang dijanjikan.

Bahkan pada tahap penulisan aplikasi, saya memiliki keinginan untuk memeriksa betapa sulitnya untuk port ke platform lain. Mari kita mulai dengan ponsel.

Tentunya ada cara yang tidak biadab, tapi saya memutuskan bahwa jalan terpendek adalah langsung. Oleh karena itu, saya cukup membuat proyek Flutter baru, mentransfer file pubspec.yaml , assets , fonts dan direktori lib ke sana dan menambahkan baris ke AndroidManifest.xml :

 <uses-permission android:name="android.permission.INTERNET" /> 

Aplikasi dimulai dengan setengah tendangan dan saya mendapatkan ini

gambar


Pada awalnya, saya harus mengotak-atik web. Saya tidak tahu cara membuat proyek web, jadi saya menggunakan instruksi dari Internet, yang karena beberapa alasan tidak berhasil. Saya ingin meludah, tetapi menemukan manual ini .

Akibatnya, semuanya ternyata sesederhana mungkin - hanya perlu menyertakan dukungan untuk aplikasi web. Peras dari manual:

 flutter channel master flutter upgrade flutter config --enable-web cd <into project directory> flutter create . flutter run -d chrome 

Kemudian saya mentransfer file-file yang diperlukan ke proyek ini dengan cara barbar yang sama dan menerima itu

hasil


Kesan umum


Pada awalnya, bekerja dengan Flutter tidak biasa, saya terus-menerus mencoba menggunakan pendekatan yang biasa dari React Native, dan ini mengganggu. Juga, beberapa redundansi kode panah agak mengganggu.

Setelah saya mendapatkan tangan saya (dan kerucut) sedikit, saya mulai melihat keuntungan dari Flutter over React Native. Saya akan daftar beberapa.

Bahasa Dart adalah bahasa yang sepenuhnya dapat dimengerti dan menyenangkan dengan pengetikan statis yang kuat. Setelah JavaScript, rasanya seperti menghirup udara segar. Saya berhenti takut kode saya akan rusak saat runtime dan itu adalah perasaan yang menyenangkan. Seseorang mungkin mengatakan bahwa ada Flow dan TypeScript, tetapi bukan itu - dalam proyek kami, kami menggunakan keduanya, dan sesuatu yang lain selalu rusak di suatu tempat. Ketika saya menulis di React Native, saya tidak dapat membantu tetapi merasa bahwa kode saya ada di alat peraga batang korek api yang dapat pecah kapan saja. Dengan Flutter, saya lupa perasaan ini, dan jika harganya redundansi kode, maka saya siap untuk membayarnya.

Platform . Di Bereaksi Asli, Anda menggunakan komponen asli dan ini umumnya baik. Tetapi karena ini, Anda terkadang harus menulis kode khusus platform, serta menangkap bug khusus untuk setiap platform. Ini bisa sangat melelahkan. Dengan Flutter, Anda dapat melupakan masalah ini seperti mimpi buruk (walaupun mungkin dalam aplikasi besar hal-hal yang tidak begitu lancar).

Lingkungan . Dengan lingkungan di React Native, semuanya menyedihkan. Plugin vscode terus-menerus jatuh, debugger dapat melahap 16 gig operatif dan 70 gig swap, dengan ketat menggantung sistem (dari pengalaman pribadi), dan skenario koreksi kesalahan paling umum: "hapus node_modules, instal paket lagi dan coba restart beberapa kali." Ini biasanya membantu, tetapi bljad! Tidak seharusnya begitu, tidak begitu.

Selain itu, Anda harus menjalankan AndroidStudio dan Xcode secara berkala, karena beberapa hanya diletakkan dengan cara ini (adil, dengan rilis RN 0,60 ini menjadi lebih baik).

Terhadap latar belakang ini, plugin Flutter resmi untuk vscode terlihat sangat bagus. Petunjuk untuk kode memungkinkan Anda untuk berkenalan dengan platform tanpa melihat dokumentasi, pemformatan otomatis memecahkan masalah dengan gaya pengkodean, debugger normal, dll.
Secara umum, ini terlihat seperti alat yang lebih matang.

Lintas-platform . Bereaksi Asli mengakui prinsip "Belajar sekali, menulis di mana-mana" - setelah Anda belajar, Anda dapat menulis untuk platform yang berbeda. Benar, di bawah setiap platform Anda akan menemui masalah khusus untuk itu. Tapi mungkin ini hanya konsekuensi dari ketidakdewasaan React Native - saat ini, versi stabil terbaru adalah 0,61. Mungkin dengan rilis versi 1.0, sebagian besar masalah ini akan hilang.

Pendekatan Flutter lebih mirip Write sekali, kompilasi di mana-mana. Dan bahkan jika desktop tidak siap untuk produksi saat ini, web juga dalam alpha, tetapi semuanya berjalan untuk itu. Dan kemampuan untuk memiliki basis kode tunggal untuk semua platform adalah argumen yang kuat.

Tentu saja, Flutter juga bukan tanpa cacat, tetapi sedikit pengalaman dalam menggunakannya tidak memungkinkan saya untuk mengidentifikasi mereka. Jadi, jika Anda ingin penilaian yang lebih objektif - silakan diskon efek kebaruan.

Secara umum, perlu dicatat bahwa Flutter meninggalkan sebagian besar perasaan positif, meskipun ia memiliki ruang untuk tumbuh. Dan proyek selanjutnya saya akan lebih bersedia untuk memulai, dan bukan pada React Native.

Kode sumber untuk proyek dapat ditemukan di GitHub .

PS Saya mengambil kesempatan ini untuk memberi selamat kepada semua yang terlibat di Hari Guru yang lalu.

Source: https://habr.com/ru/post/id470251/


All Articles