
Artikel ini akan fokus pada implementasi klien seluler Flutter.
Klien seluler yang mana?
Publikasi sebelumnya menjelaskan sistem aksesori perangkat lunak:
bobaoskit - aksesoris, dnssd dan WebSocket .
Analog dari aksesori perangkat lunak adalah objek nyata. Bola lampu, sakelar, pemutar cd / kaset, pemutar radio, termostat, sensor suhu, sensor gerak, dll ... Seperangkat aksesori ditentukan oleh imajinasi dan kode program. Anda dapat menerapkan setidaknya papan catur. Untuk papan seperti itu, Anda perlu memiliki bidang kontrol ( control
) move
, yang mengambil objek { from: "e2", to: "e4" }
misalnya, dan bidang layanan untuk mengatur ulang angka, dll. ... Skrip aksesori akan memproses permintaan untuk mengendalikan bidang move
, menerima keputusannya adalah apakah mungkin untuk memindahkan gambar, dan akan mengembalikan (atau tidak) status dengan posisi angka di seluruh bidang.
Jenis aksesori yang didukung saat ini dengan fungsi minimal adalah sebagai berikut: "sakelar", "sensor suhu", "termostat", "pemutar radio".
Tentang catur, tidak akan ada diskusi lebih lanjut. Jika itu menarik dan dalam hal ini, selamat datang di kucing.
Jadi bobaoskit.worker
berjalan. Objek aksesori ada di memori komputer, Anda dapat membaca informasi tentangnya, Anda dapat secara manual mengirim permintaan JSON
ke port WebSocket
, dan melihat acara yang masuk.
Untuk manajemen, saya membuat aplikasi seluler paling sederhana.
Mengapa bergetar?
Selama beberapa tahun terakhir, sebuah ide telah secara aktif hidup di kepala saya untuk mempelajari pemrograman untuk perangkat seluler. Karena saya menulis dalam JavaScript
, saya mempelajari solusi yang memungkinkan Anda untuk tidak belajar bahasa pemrograman baru.
Appcelerator
Dia mulai belajar dengannya. Jika memori tidak berubah, SDK terbuka, tetapi IDE dengan berbagai tarif.
NativeScript
Di sini saya telah membuat aplikasi sederhana yang menunjukkan daftar dengan gambar. Itu tidak melangkah lebih jauh.
ReactNative
Serangan terpanjang dari kerangka kerja yang tercantum sejauh ini. Tantangan terbesar adalah memulai. Saya melihat kursus. Awalnya jelas, menarik, ternyata. Tetapi redux
dan setelah kekuasaan gagal. Kemudian ia secara teratur mencoba untuk mulai menulis, tetapi redux
keras kepala tidak membiarkan dirinya untuk mengalahkan.
Akibatnya, saya tidak terikat pada keputusan apa pun saat itu (akhir 2016). Mungkin karena tidak ada tugas khusus, mungkin karena alasan lain.
Mendekati jatuhnya masa lalu (2018), pekerjaan sudah berlangsung di SDK untuk aksesori perangkat lunak. Secara alami, Anda memerlukan aplikasi seluler. Semuanya dimulai dengan mdns. Setelah waktu luang, saya memperbarui ReactNative, menemukan plugin react-native-zeroconf, menciptakan aplikasi. Menurut instruksi, diinstal, dibuat link
, diluncurkan. Aplikasi debugging Expo diluncurkan, yang tidak mendukung modul asli, dan, karenanya, plugin mdns tidak berfungsi. Pada titik ini, tidak ada cukup waktu luang untuk membuat aplikasi asli yang bersih (tanpa expo) dan mengujinya. Pekerjaan itu ditunda selama beberapa bulan.
Pada saat yang sama, semakin banyak materi muncul tentang flutter
di jaringan. Saya menginstal sendiri. Instalasi sederhana: git clone
dan tambahkan ke PATH
. Sisanya sudah menyiapkan Android SDK / Xcode (dalam kasus saya, Android SDK telah diatur untuk waktu yang lama. Saya tidak dapat mengembangkan untuk iOS, karena saya bukan pengguna makro) dan Dart SDK (dapat diinstal secara terpisah, tetapi tidak harus, karena merupakan bagian dari flutter).
Prinsip / skema kerja
- Ketika diluncurkan, aplikasi mencari layanan
_bobaoskit._tcp
di jaringan lokal menggunakan plugin flutter_mdns . Ada beberapa versi plugin ini, semuanya berakar dari yang diterbitkan , tetapi tidak kompatibel dengan versi baru dari SDK Dart, masing-masing, banyak bercabang dan ditambahkan kompatibilitas. Saya memilih versi ini karena yang lain tidak menyelesaikan host dari beberapa layanan yang ditemukan sekaligus.
Setelah deteksi dan penentuan (onResolve), host ditambahkan ke daftar.
Halaman dengan daftar layanan yang ditemukan adalah StatefulWidget
, masing-masing, ketika mendeteksi / kehilangan layanan, setState() {...}
disebut - Ketika memilih host dari daftar, halaman baru dibuat (juga
StatefulWidget
), yang host
dan port
layanan yang dipilih ditransmisikan.
Objek BobaosKit
bertanggung jawab untuk komunikasi dibuat. Respons diproses melalui callback, sebagai sementara saya tidak belajar banyak panah asinkron. Tetapi dilihat dari dokumentasi yang dipindai, Futures
adalah analog dari Promise
di JS.
Fungsi direkam untuk acara yang masuk (tidak ada tanggapan). EventEmitter
untuk Dart di sini. Saya menulis yang sangat sederhana.
void registerListener(String name, Function cb) { this._events.add(new BobaosKitCallback(name, cb)); } void removeAllListeners() { this._events = []; } void emitEvent(String name, dynamic params) { // call all listeners List<BobaosKitCallback> foundCallbacks = this._events.where((t) => t.name == name).toList(); foundCallbacks.forEach((f) => f.cb(params)); } ... ... void listen() { this.ws.listen((text) { var json = jsonDecode(text); if (json.containsKey('response_id')) { .... } else { // response_id - this.emitEvent(json['method'], json['payload']); } }); }
Acara masuk - jika aksesori telah dilepas, ditambahkan, status diperbarui. Atau jika semua aksesori dilepas ( clear accessories
).
Fungsi terdaftar - untuk memperbarui daftar, widget untuk acara ini.
- Permintaan dikirim untuk informasi tentang semua aksesori.
Objek AccessoryInfo dibuat untuk setiap aksesori:
import 'package:scoped_model/scoped_model.dart'; // AccessoryInfo extends Model // so, when accessory value is updated it descends down to // all widgets inside ScopedModelDescendant class AccessoryInfo extends Model { dynamic id; dynamic type; String name; String job_channel; List control; List status; bool selected; Map<dynamic, dynamic> currentState; AccessoryInfo(Map<dynamic, dynamic> obj) { this.id = obj['id']; this.type = obj['type']; this.name = obj['name']; this.job_channel = obj['job_channel']; this.control = obj['control']; this.status = obj['status']; this.currentState = {}; } void updateCurrentState(key, value) { currentState[key] = value; notifyListeners(); } void notify() { notifyListeners(); } }
objek ini sudah menjadi model. Awalnya, saya menulis StatefulWidget
dan setState() {}
mana-mana, tetapi setState() {}
hanya berfungsi untuk widget di dalamnya pendengar terdaftar. Tetapi untuk manajemen aksesori terperinci, pada awalnya saya membuat halaman Stateful
baru, dan memperhatikan bahwa statusnya tidak diperbarui. Sebagai solusi - digunakan ScopedModel
.
Setelah daftar aksesori diterima, untuk masing-masing kami mengirimkan permintaan status dan menambahkan ke daftar List <AccessoryInfo>
. Panggil setState() {}
, dengan demikian menambahkan aksesori yang didukung ke antarmuka. Jenis aksesori yang didukung didefinisikan dalam ListView.builder
dan di ./lib/widgets/*.dart
. switch/temperature sensor/radio player/thermostat
ini didukung. Pekerjaan utama ke depan adalah menambahkan yang baru dan meningkatkan widget yang ada.
- Sekarang tentang cara membuat elemen terpisah untuk setiap aksesori. Misalnya, pertimbangkan sakelar.
@override Widget build(BuildContext context) { return new ScopedModel<AccessoryInfo>( model: info, child: ScopedModelDescendant<AccessoryInfo>( builder: (context, child, model) { var cardColor = Theme.of(context).cardColor; dynamic switchState = model.currentState['state']; if (switchState is bool) { if (switchState) { cardColor = Colors.deepPurple; } else { cardColor = Theme.of(context).cardColor; } } return Card( color: cardColor, child: ListTile( selected: false, leading: new Icon(Icons.lightbulb_outline), title: new Text("${model.name}"), onTap: () { // to control accessory value // get status value at first bobaos.getStatusValue( model.id, "state", (bool err, Object payload) { if (err) { return print('error ocurred $payload'); } if (payload is Map) { dynamic currentValue = payload['status']['value']; bool newValue; if (currentValue is bool) { // invert newValue = !currentValue; } else { newValue = false; } // then send new value bobaos.controlAccessoryValue( model.id, {"state": newValue}, (bool err, Object payload) { if (err) { return print('error ocurred $payload'); } }); } }); }, onLongPress: () { // TODO: dialog with additional funcs }, )); })); }
Untuk aksesori jenis switch
, elemen dibuat dalam daftar aksesori umum, ketika berinteraksi dengan yang (onTap) permintaan dikirim untuk mendapatkan nilai saat ini, kemudian untuk beralih nilai ini. ScopedModel
memungkinkan ScopedModel
untuk menggambar ulang widget untuk pembaruan status yang masuk.
Handler klik panjang tidak diterapkan untuk aksesori ini.
Untuk pemutar radio, tampilannya seperti ini:
onLongPress: () { Navigator.of(context).push(MaterialPageRoute( builder: (context) => AccRadioPlayerControl( info: info, bobaos: bobaos, ))); },
Halaman AccRadioPlayerControl
, yang juga menggunakan ScopedModel
untuk mengelola keadaan.
Pada ini, seluruh deskripsi algoritma operasi program habis. Tidak ada fitur tambahan, seperti mengingat host terakhir, memilah aksesori ke dalam kategori / kamar tidak diimplementasikan. Saat ini, semuanya sederhana.
Masalahnya
Saya akan menjelaskan masalah utama yang ada sekarang. Saya masih tidak mengerti bagaimana cara mendeteksi koneksi WebSocket
rusak.
Saya menggunakan: kelas WebSocket .
Ketika aplikasi / perangkat dalam mode tidur untuk waktu yang lama, koneksi terputus. Anda harus kembali ke halaman pertama dan membuka kembali layanan yang ditemukan.
Kata penutup
Di satu sisi, Flutter cukup cepat dalam belajar dan berkembang. ScopedModel ternyata lebih bisa dimengerti oleh saya daripada redux.
Dart ternyata mirip dengan JavaScript yang biasa. Mengetik + tipe dinamis akan memungkinkan semua orang untuk menulis dengan nyaman.
Kesulitan dalam menulis kode: bersarangnya widget yang besar. Panggilan balik neraka yang terkenal setelah bergetar terlihat berbeda. Mode-vim dan %
akan berguna.
Sekarang beberapa pemikiran tentang IOT. Baru-baru ini, semakin banyak perangkat / layanan pintar yang memerlukan pendaftaran di cloud. Outlet Cina, untuk penggunaan yang Anda perlukan untuk menginstal aplikasi, membuat akun, dan hanya setelah itu Anda dapat menggunakannya.
Asisten suara. Alice dari Yandex membutuhkan cloud-nya ke mana teks yang dikenali dikirim. Amazon Alexa bekerja dengan cara yang sama.
Yang paling sukses, menurut saya, dibuat oleh Apple HomeKit bersama dengan Siri. Cloud digunakan untuk pengenalan teks. Interaksi dengan perangkat - di jaringan lokal.
Pendapat saya adalah bahwa cloud harus ada untuk tujuannya: remote control, memperbarui, dll ... Jika perangkat dapat dikontrol pada jaringan lokal, maka Anda perlu melakukan ini.
Referensi
- Repositori aplikasi
- Dokumentasi Bobaoskit - menjelaskan cara menginstal bobaoskit.worker dan meluncurkan aksesori
radio player
.