Memecahkan Masalah Umum Lintas-Platform Saat Bekerja Dengan Flutter
Diterbitkan: 2022-03-10Saya telah melihat banyak kebingungan online mengenai pengembangan Web dengan Flutter dan, seringkali, sayangnya karena alasan yang salah.
Secara khusus, orang terkadang mengacaukannya dengan kerangka kerja lintas platform seluler (dan desktop) berbasis Web yang lebih lama, yang pada dasarnya hanya halaman Web yang berjalan di dalam browser yang berjalan di dalam aplikasi pembungkus.
Itu benar-benar lintas platform dalam arti bahwa antarmuka tetap sama karena Anda hanya memiliki akses ke antarmuka yang biasanya dapat diakses di Web.
Flutter bukan itu, meskipun: itu berjalan secara asli di setiap platform, dan itu berarti setiap aplikasi berjalan seperti jika ditulis dalam Java/Kotlin atau Objective-C/Swift di Android dan iOS, cukup banyak. Anda perlu tahu itu karena ini menyiratkan bahwa Anda perlu memperhatikan banyak perbedaan antara platform yang sangat beragam ini.
Pada artikel ini, kita akan melihat beberapa perbedaan tersebut dan cara mengatasinya. Lebih khusus lagi, kita akan berbicara tentang penyimpanan dan perbedaan UI, yang merupakan perbedaan yang paling sering menyebabkan kebingungan bagi pengembang saat menulis kode Flutter yang mereka inginkan untuk menjadi lintas platform.
Contoh 1: Penyimpanan
Saya baru-baru ini menulis di blog saya tentang perlunya pendekatan berbeda untuk menyimpan JWT di aplikasi Web jika dibandingkan dengan aplikasi seluler.
Itu karena sifat yang berbeda dari opsi penyimpanan platform, dan kebutuhan untuk mengetahui masing-masing dan alat pengembangan asli mereka.
Web
Saat Anda menulis aplikasi Web, opsi penyimpanan yang Anda miliki adalah:
- mengunduh/mengunggah file ke/dari disk, yang memerlukan interaksi pengguna dan oleh karena itu hanya cocok untuk file yang dimaksudkan untuk dibaca atau dibuat oleh pengguna;
- menggunakan cookie, yang mungkin atau mungkin tidak dapat diakses dari JS (tergantung pada apakah itu
httpOnly
) dan secara otomatis dikirim bersama dengan permintaan ke domain tertentu dan disimpan ketika mereka datang sebagai bagian dari tanggapan; - menggunakan JS
localStorage
dansessionStorage
, dapat diakses oleh JS mana pun di situs web, tetapi hanya dari JS yang merupakan bagian dari halaman situs web tersebut.
Seluler
Situasi ketika datang ke aplikasi seluler benar-benar berbeda. Opsi penyimpanan adalah sebagai berikut:
- dokumen aplikasi lokal atau penyimpanan cache, dapat diakses oleh aplikasi itu;
- jalur penyimpanan lokal lainnya untuk file yang dibuat pengguna/dapat dibaca;
-
NSUserDefaults
danSharedPreferences
masing-masing di iOS dan Android untuk penyimpanan nilai kunci; -
Keychain
di iOS danKeyStore
di Android untuk penyimpanan aman, masing-masing, data dan kunci kriptografi apa pun.
Jika Anda tidak tahu itu, Anda akan mengacaukan implementasi Anda karena Anda perlu tahu solusi penyimpanan apa yang sebenarnya Anda gunakan dan apa kelebihan dan kekurangannya.
Solusi Lintas Platform: Pendekatan Awal
Menggunakan paket Flutter shared_preferences
menggunakan localStorage
di Web, SharedPreferences
di Android dan NSUserDefaults
di iOS. Itu memiliki implikasi yang sama sekali berbeda untuk aplikasi Anda, terutama jika Anda menyimpan informasi sensitif seperti token sesi: localStorage
dapat dibaca oleh klien, jadi itu masalah jika Anda rentan terhadap XSS. Meskipun aplikasi seluler tidak terlalu rentan terhadap XSS, SharedPreferences
dan NSUserDefaults
bukanlah metode penyimpanan yang aman karena dapat disusupi di sisi klien karena bukan penyimpanan yang aman dan tidak dienkripsi. Itu karena mereka dimaksudkan untuk preferensi pengguna, seperti yang disebutkan di sini dalam kasus iOS dan di sini di dokumentasi Android ketika berbicara tentang perpustakaan Keamanan yang dirancang untuk menyediakan pembungkus ke SharedPreferences
khusus untuk mengenkripsi data sebelum menyimpannya.
Penyimpanan Aman Di Seluler
Satu-satunya solusi penyimpanan aman di ponsel masing-masing adalah Keychain
dan KeyStore
di iOS dan Android, sedangkan tidak ada penyimpanan aman di Web .
Namun, Keychain
dan KeyStore
sangat berbeda sifatnya: Keychain
adalah solusi penyimpanan kredensial generik, sedangkan KeyStore
digunakan untuk menyimpan (dan dapat menghasilkan) kunci kriptografik , baik kunci simetris atau kunci publik/pribadi.
Ini berarti bahwa jika, misalnya, Anda perlu menyimpan token sesi, di iOS Anda dapat membiarkan OS mengelola bagian enkripsi dan hanya mengirim token Anda ke Keychain
, sedangkan di Android ini sedikit lebih merupakan pengalaman manual karena Anda memerlukan untuk membuat (bukan hard-code, itu buruk) sebuah kunci, menggunakannya untuk mengenkripsi token, menyimpan token terenkripsi di SharedPreferences
dan menyimpan kunci di KeyStore
.
Ada pendekatan yang berbeda untuk itu, seperti kebanyakan hal dalam keamanan, tetapi yang paling sederhana mungkin menggunakan enkripsi simetris, karena tidak perlu kriptografi kunci publik karena aplikasi Anda mengenkripsi dan mendekripsi token.
Jelas, Anda tidak perlu menulis kode khusus platform seluler yang melakukan semua itu, karena ada plugin Flutter yang melakukan semua itu, misalnya.
Kurangnya Penyimpanan Aman Di Web
Sebenarnya itulah alasan yang mendorong saya untuk menulis postingan ini. Saya menulis tentang menggunakan paket itu untuk menyimpan JWT di aplikasi seluler dan orang-orang menginginkan versi Web itu tetapi, seperti yang saya katakan, tidak ada penyimpanan aman di Web . Itu tidak ada.
Apakah itu berarti JWT Anda harus terbuka?
Tidak, tidak sama sekali. Anda dapat menggunakan cookie httpOnly
, bukan? Itu tidak dapat diakses oleh JS dan hanya dikirim ke server Anda. Masalahnya adalah bahwa mereka selalu dikirim ke server Anda, bahkan jika salah satu pengguna Anda mengklik URL permintaan GET di situs web orang lain dan permintaan GET itu memiliki efek samping yang tidak Anda atau pengguna Anda sukai. Ini sebenarnya berfungsi untuk jenis permintaan lain juga, hanya saja lebih rumit. Ini disebut Pemalsuan Permintaan Lintas Situs dan Anda tidak menginginkannya. Ini adalah salah satu ancaman keamanan web yang disebutkan dalam dokumen MDN Mozilla, di mana Anda dapat menemukan penjelasan yang lebih lengkap.
Ada metode pencegahan. Yang paling umum adalah memiliki dua token, sebenarnya: salah satunya sampai ke klien sebagai cookie httpOnly
, yang lain sebagai bagian dari respons. Yang terakhir harus disimpan di localStorage
dan bukan di cookie karena kami tidak ingin itu dikirim secara otomatis ke server.
Memecahkan Keduanya
Bagaimana jika Anda memiliki aplikasi seluler dan aplikasi Web?
Itu dapat ditangani dengan salah satu dari dua cara:
- Gunakan titik akhir backend yang sama, tetapi dapatkan dan kirim cookie secara manual menggunakan header HTTP terkait cookie;
- Buat titik akhir backend non-Web terpisah yang menghasilkan token berbeda dari salah satu token yang digunakan oleh aplikasi Web, lalu izinkan otorisasi JWT reguler jika klien dapat memberikan token khusus seluler.
Menjalankan Kode Berbeda Pada Platform Berbeda
Sekarang, mari kita lihat bagaimana kita dapat menjalankan kode yang berbeda pada platform yang berbeda agar dapat mengimbangi perbedaan tersebut.
Membuat Plugin Flutter
Khusus untuk mengatasi masalah penyimpanan, salah satu cara yang dapat Anda lakukan adalah dengan paket plugin: plugin menyediakan antarmuka Dart yang umum dan dapat menjalankan kode yang berbeda pada platform yang berbeda, termasuk kode Kotlin/Java khusus platform asli atau kode Swift/Objective-C . Mengembangkan paket dan plugin agak rumit, tetapi dijelaskan di banyak tempat di Web dan di tempat lain (misalnya di buku Flutter), termasuk dokumentasi resmi Flutter.
Untuk platform seluler, misalnya, sudah ada plugin penyimpanan aman, dan itu flutter_secure_storage
, yang contoh penggunaannya dapat Anda temukan di sini, tetapi itu tidak berfungsi di Web, misalnya.
Di sisi lain, untuk penyimpanan nilai kunci sederhana yang juga berfungsi di web, ada paket plugin pihak pertama lintas platform yang dikembangkan Google bernama shared_preferences
, yang memiliki komponen khusus Web yang disebut shared_preferences_web
yang menggunakan NSUserDefaults
, SharedPreferences
atau localStorage
tergantung pada platformnya.
TargetPlatform di Flutter
Setelah mengimpor package:flutter/foundation.dart
, Anda dapat membandingkan Theme.of(context).platform
dengan nilai:
-
TargetPlatform.android
-
TargetPlatform.iOS
-
TargetPlatform.linux
-
TargetPlatform.windows
-
TargetPlatform.macOS
-
TargetPlatform.fuchsia
dan tulis fungsi Anda sehingga, untuk setiap platform yang ingin Anda dukung, mereka melakukan hal yang sesuai. Ini akan sangat berguna untuk contoh perbedaan platform berikutnya, dan itu adalah perbedaan dalam cara widget ditampilkan pada platform yang berbeda.
Untuk kasus penggunaan itu, khususnya, ada juga plugin flutter_platform_widgets
yang cukup populer, yang menyederhanakan pengembangan widget platform-aware.
Contoh 2: Perbedaan Bagaimana Widget yang Sama Ditampilkan
Anda tidak bisa hanya menulis kode lintas platform dan berpura-pura browser, telepon, komputer, dan jam tangan pintar adalah hal yang sama — kecuali jika Anda ingin aplikasi Android dan iOS Anda menjadi WebView dan aplikasi desktop Anda dibuat dengan Electron . Ada banyak alasan untuk tidak melakukan itu, dan bukan inti dari bagian ini untuk meyakinkan Anda untuk menggunakan kerangka kerja seperti Flutter sebagai gantinya yang menjaga aplikasi Anda tetap asli, dengan semua keunggulan kinerja dan pengalaman pengguna yang menyertainya, sambil memungkinkan Anda untuk tulis kode yang akan sama untuk semua platform hampir sepanjang waktu.
Namun, itu membutuhkan perhatian dan perhatian, dan setidaknya pengetahuan dasar tentang platform yang ingin Anda dukung, API asli mereka yang sebenarnya, dan semua itu. Pengguna React Native perlu lebih memperhatikan hal itu karena kerangka kerja tersebut menggunakan widget OS bawaan, jadi Anda sebenarnya perlu lebih memperhatikan tampilan aplikasi dengan mengujinya secara ekstensif di kedua platform, tanpa dapat beralih di antara Widget iOS dan Material dengan cepat seperti yang dimungkinkan dengan Flutter.
Apa yang Berubah Tanpa Permintaan Anda
Ada beberapa aspek UI aplikasi Anda yang otomatis berubah saat Anda berpindah platform. Bagian ini juga menyebutkan perubahan apa yang terjadi antara Flutter dan React Native dalam hal ini.
Antara Android dan iOS (Flutter)
Flutter mampu merender widget Material di widget iOS (dan Cupertino (mirip iOS) di Android), tetapi yang TIDAK dilakukan adalah menampilkan hal yang persis sama di Android dan iOS: Tema material secara khusus menyesuaikan dengan konvensi setiap platform .
Misalnya, animasi dan transisi navigasi dan font default berbeda, tetapi itu tidak terlalu memengaruhi aplikasi Anda.
Apa yang mungkin memengaruhi beberapa pilihan Anda dalam hal estetika atau UX adalah kenyataan bahwa beberapa elemen statis juga berubah. Secara khusus, beberapa ikon berubah di antara kedua platform, judul bilah aplikasi di tengah di iOS dan di kiri di Android (di kiri ruang yang tersedia jika ada tombol kembali atau tombol untuk membuka Laci (dijelaskan di sini) dalam panduan Desain Material dan juga dikenal sebagai menu hamburger). Berikut tampilan aplikasi Material dengan Laci di Android:
Dan seperti apa aplikasi Material yang sama dan sangat sederhana di iOS:
Antara Seluler dan Web dan Dengan Takik Layar (Flutter)
Di Web ada sedikit situasi yang berbeda, seperti yang disebutkan juga dalam artikel Smashing tentang Pengembangan Web Responsif dengan Flutter: khususnya, selain harus mengoptimalkan layar yang lebih besar dan memperhitungkan cara yang diharapkan orang untuk menavigasi situs Anda — yang merupakan fokus utama artikel itu — Anda harus khawatir tentang fakta bahwa terkadang widget ditempatkan di luar jendela browser. Selain itu, beberapa ponsel memiliki lekukan di bagian atas layarnya atau halangan lain untuk tampilan yang benar dari aplikasi Anda karena semacam penghalang.
Kedua masalah ini dapat dihindari dengan membungkus widget Anda dalam widget SafeArea
, yang merupakan jenis widget padding tertentu yang memastikan widget Anda berada di tempat yang benar-benar dapat ditampilkan tanpa apa pun yang menghalangi kemampuan pengguna untuk melihatnya, baik itu kendala perangkat keras atau perangkat lunak.
Dalam Bereaksi Asli
React Native membutuhkan lebih banyak perhatian dan pengetahuan yang jauh lebih dalam dari setiap platform, selain mengharuskan Anda untuk menjalankan Simulator iOS serta Android Emulator setidaknya untuk dapat menguji aplikasi Anda di kedua platform: tidak sama dan mengonversi elemen UI JavaScript-nya menjadi widget khusus platform. Dengan kata lain, aplikasi React Native Anda akan selalu terlihat seperti iOS — dengan elemen UI Cupertino seperti yang kadang-kadang disebut — dan aplikasi Android Anda akan selalu terlihat seperti aplikasi Android Material Design biasa karena menggunakan widget platform.
Perbedaannya di sini adalah Flutter merender widgetnya dengan mesin rendering level rendahnya sendiri, yang berarti Anda dapat menguji kedua versi aplikasi pada satu platform.
Mengatasi Masalah Itu
Kecuali Anda menginginkan sesuatu yang sangat spesifik, aplikasi Anda seharusnya terlihat berbeda pada platform yang berbeda jika tidak, beberapa pengguna Anda tidak akan senang.
Sama seperti Anda seharusnya tidak hanya mengirimkan aplikasi seluler ke web (seperti yang saya tulis di posting Smashing yang disebutkan di atas), Anda tidak boleh mengirimkan aplikasi yang penuh dengan widget Cupertino ke pengguna Android, misalnya, karena itu akan membingungkan bagi pengguna Android. sebagian besar. Di sisi lain, memiliki kesempatan untuk benar-benar menjalankan aplikasi yang memiliki widget yang dimaksudkan untuk platform lain memungkinkan Anda menguji aplikasi dan menunjukkannya kepada orang-orang di kedua versi tanpa harus menggunakan dua perangkat untuk itu.
Sisi Lain: Menggunakan Widget yang Salah Untuk Alasan yang Tepat
Tetapi itu juga berarti bahwa Anda dapat melakukan sebagian besar pengembangan Flutter Anda di workstation Linux atau Windows tanpa mengorbankan pengalaman pengguna iOS Anda, dan kemudian cukup membangun aplikasi untuk platform lain dan tidak perlu khawatir untuk mengujinya secara menyeluruh.
Langkah selanjutnya
Kerangka kerja lintas platform memang mengagumkan, tetapi mereka mengalihkan tanggung jawab kepada Anda, pengembang, untuk memahami cara kerja setiap platform dan cara memastikan aplikasi Anda beradaptasi dan nyaman digunakan bagi pengguna Anda. Hal-hal kecil lainnya yang perlu dipertimbangkan mungkin, misalnya, menggunakan deskripsi yang berbeda untuk apa yang pada dasarnya mungkin sama jika ada konvensi yang berbeda pada platform yang berbeda.
Sangat bagus untuk tidak harus membangun dua (atau lebih) aplikasi secara terpisah menggunakan bahasa yang berbeda, tetapi Anda tetap perlu mengingat bahwa Anda, pada dasarnya, membangun lebih dari satu aplikasi dan itu membutuhkan pemikiran tentang setiap aplikasi yang Anda buat .
Sumber Daya Lebih Lanjut
- Situs web Galeri Flutter dan aplikasi Android, menampilkan penggunaan widget Flutter yang khas dari berbagai platform dan agnostisisme platformnya
- Dokumentasi Flutter API di TargetPlatform
- Dokumentasi Flutter tentang pembuatan paket dan plugin
- Dokumentasi Flutter tentang adaptasi platform
- Dokumentasi MDN pada cookie