Ketergantungan Injeksi dalam Flutter

Kami saat ini bereksperimen dengan Flutter sambil mengembangkan proyek sampingan kami untuk tantangan langkah dengan kolega. Proyek sampingan ini juga harus dianggap sebagai taman bermain, di mana kita dapat memeriksa apakah kita dapat menggunakan Flutter dalam proyek yang lebih serius. Itu sebabnya kami ingin menggunakan beberapa pendekatan di sana yang dapat terlihat seperti rekayasa berlebihan untuk proyek sekecil itu.


Jadi salah satu pertanyaan pertama adalah apa yang bisa kita gunakan untuk injeksi ketergantungan. Pencarian cepat di internet mengungkapkan 2 perpustakaan dengan ulasan positif: get_it dan kiwi . Karena get_it ternyata adalah Service Locator (dan saya bukan penggemar pola ini), saya akan bermain dengan kiwi, yang terlihat lebih menjanjikan, tapi kemudian saya menemukan satu perpustakaan lagi: inject.dart . Ini sangat terinspirasi oleh perpustakaan Dagger, dan saat kami menggunakan yang terbaru di proyek Android kami yang lain, saya memutuskan untuk menggali lebih dalam.


Patut dikatakan bahwa meskipun perpustakaan ini terletak di repositori Google GitHub, ini bukan perpustakaan resmi dari Google dan saat ini tidak ada dukungan yang diberikan:


Pustaka ini saat ini ditawarkan apa adanya (pratinjau pengembang) karena bersumber terbuka dari repositori internal di dalam Google. Karena itu kami tidak dapat menindak bug atau permintaan fitur saat ini.

Meskipun demikian, sepertinya perpustakaan melakukan semua yang kami butuhkan untuk saat ini, jadi saya ingin membagikan beberapa info tentang bagaimana Anda dapat menggunakan perpustakaan ini dalam proyek Anda.


Instalasi


Karena tidak ada paket di repositori resmi , kami harus menginstalnya secara manual. Saya lebih suka melakukannya sebagai submodule git, jadi saya membuat folder vendor di direktori sumber proyek saya dan menjalankan perintah berikut dari direktori ini:


 git submodule add https://github.com/google/inject.dart 

Dan sekarang kita dapat mengaturnya dengan menambahkan baris berikut ke pubspec.yaml :


 dependencies: // other dependencies here inject: path: ./vendor/inject.dart/package/inject dev_dependencies: // other dev_dependencies here build_runner: ^1.0.0 inject_generator: path: ./vendor/inject.dart/package/inject_generator 

Penggunaan


Fungsi apa yang biasanya kita harapkan dari perpustakaan DI? Mari kita membahas beberapa kasus penggunaan umum:


Injeksi kelas beton


Ini bisa sesederhana ini:


 import 'package:inject/inject.dart'; @provide class StepService { // implementation } 

Kita dapat menggunakannya misalnya dengan widget Flutter seperti ini:


 @provide class SomeWidget extends StatelessWidget { final StepService _service; SomeWidget(this._service); } 

Injeksi antarmuka


Pertama-tama kita perlu mendefinisikan kelas abstrak dengan beberapa kelas implementasi, misalnya:


 abstract class UserRepository { Future<List<User>> allUsers(); } class FirestoreUserRepository implements UserRepository { @override Future<List<User>> allUsers() { // implementation } } 

Dan sekarang kami dapat menyediakan dependensi dalam modul kami:


 import 'package:inject/inject.dart'; @module class UsersServices { @provide UserRepository userRepository() => FirestoreUserRepository(); } 

Penyedia


Apa yang harus dilakukan jika kita tidak perlu menginstruksikan beberapa kelas, melainkan penyedia, yang akan memberi kita instance baru dari kelas ini setiap kali? Atau jika kita perlu menyelesaikan ketergantungan dengan malas alih-alih mendapatkan contoh konkret di konstruktor? Saya tidak menemukannya dalam dokumentasi (well, karena tidak ada dokumentasi sama sekali) maupun dalam contoh yang diberikan, tetapi sebenarnya berfungsi dengan cara ini sehingga Anda dapat meminta fungsi mengembalikan instance yang diperlukan dan itu akan disuntikkan dengan benar.


Kami bahkan dapat mendefinisikan jenis pembantu seperti ini:


 typedef Provider<T> = T Function(); 

dan menggunakannya di kelas kami:


 @provide class SomeWidget extends StatelessWidget { final Provider<StepService> _service; SomeWidget(this._service); void _someFunction() { final service = _service(); // use service } } 

Injeksi dengan bantuan


Tidak ada fungsi bawaan untuk menyuntikkan objek yang memerlukan argumen yang dikenal saat runtime saja, jadi kita dapat menggunakan pola umum dengan pabrik dalam kasus ini: membuat kelas pabrik yang mengambil semua dependensi waktu kompilasi dalam konstruktor dan menyuntikkannya, dan menyediakan metode pabrik dengan argumen runtime yang akan membuat instance yang diperlukan.


Lajang, kualifikasi, dan injeksi asinkron


Ya, perpustakaan mendukung semua ini. Sebenarnya ada penjelasan yang bagus dalam contoh resmi .


Mengabelkannya


Langkah terakhir untuk membuat semuanya berfungsi adalah membuat injector (alias komponen dari Dagger), misal seperti ini:


 import 'main.inject.dart' as g; @Injector(const [UsersServices, DateResultsServices]) abstract class Main { @provide MyApp get app; static Future<Main> create( UsersServices usersModule, DateResultsServices dateResultsModule, ) async { return await g.Main$Injector.create( usersModule, dateResultsModule, ); } } 

Di sini UserServices dan DateResultsServices adalah modul yang didefinisikan sebelumnya, MyApp adalah widget root dari aplikasi kami, dan main.inject.dart adalah file yang dibuat secara otomatis (lebih lanjut tentang ini nanti).


Sekarang kita dapat mendefinisikan fungsi utama kita seperti ini:


 void main() async { var container = await Main.create( UsersServices(), DateResultsServices(), ); runApp(container.app); } 

Lari


Karena inject berfungsi dengan pembuatan kode, kita perlu menggunakan build runner untuk menghasilkan kode yang diperlukan. Kita dapat menggunakan perintah ini:


 flutter packages pub run build_runner build 

atau watch perintah untuk menjaga kode sumber disinkronkan secara otomatis:


 flutter packages pub run build_runner watch 

Tapi ada satu momen penting di sini: secara default kode akan dihasilkan ke folder cache dan Flutter saat ini tidak mendukung ini (meskipun ada pekerjaan yang sedang berlangsung untuk menyelesaikan masalah ini). Jadi kita perlu menambahkan file inject_generator.build.yaml dengan konten berikut:


 builders: inject_generator: target: ":inject_generator" import: "package:inject_generator/inject_generator.dart" builder_factories: - "summarizeBuilder" - "generateBuilder" build_extensions: ".dart": - ".inject.summary" - ".inject.dart" auto_apply: dependents build_to: source 

Ini sebenarnya konten yang sama seperti di vendor/inject.dart/package/inject_generator/build.yaml file vendor/inject.dart/package/inject_generator/build.yaml kecuali untuk satu baris: build_to: cache telah diganti dengan build_to: source .


Sekarang kita dapat menjalankan build_runner , itu akan menghasilkan kode yang diperlukan (dan memberikan pesan kesalahan jika beberapa dependensi tidak dapat diselesaikan) dan setelah itu kita dapat menjalankan Flutter build seperti biasa.


Untung


Itu saja. Anda juga harus memeriksa contoh yang disediakan dengan perpustakaan itu sendiri, dan jika Anda memiliki pengalaman dengan perpustakaan Dagger, inject akan sangat akrab bagi Anda.

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


All Articles