Injectable
-
Hỗ trợ tự động inject khi triển khai dependency trong dart.
-
Vui lòng tìm hiểu
getIt
trước khi đọc bài này.
1. Installation
-
Thêm package vào file
pubspec.yaml
dependencies: # thêm gói injectable injectable: # thêm gói get_it get_it: dev_dependencies: # thêm gói này đễ hỗ trợ tạo code tự động injectable_generator: # add build runner if not already added build_runner:
2. Setup
-
Bước 1 : Tạo mới file dart và khai báo biến gobal
GetIt
-
Bước 2 : Khai báo method
configureDependencies()
-
Bước 3 : Thêm annotaion
@InjectableInit
-
Bước 4: Gọi configureDependencies() tại hàm main()
import '<FILE_NAME>.config.dart';
// Bước 1 : Tạo mới file dart và khai báo biến gobal `GetIt`
final getIt = GetIt.instance;
// Bước 3: Thêm annotaion `@InjectableInit`
@InjectableInit(
initializerName: 'init', // default
preferRelativeImports: true, // default
asExtension: true, // default
)
// Bước 2 : Khai báo method `configureDependencies()`
void configureDependencies() => getIt.init();
Note : Bạn có thể chỉ định folder để generate code với thuộc tính generateForDir
của @InjectableInit
@InjectableInit(generateForDir: ['test'])
void configureDependencies() => getIt.init();
void main() {
// Bước 4 : Gọi configureDependencies() tại hàm main()
configureDependencies();
runApp(MyApp());
}
3. Đăng kí factory
-
Nhắc lại : factory là dạng đăng kí mà mỗi lần bạn access đối tượng từ getIt, thì getIt sẽ tạo ra một intance mới.
-
Để đăng kí factory, bạn chỉ cần thêm annotation
@injectable
vào các class mong muốn.@injectable class StorageService {} @injectable class LanguageService { final StorageService storageService; LanguageService(this.storageService); }
-
Dùng lệnh để tạo code tự động
flutter packages pub run build_runner build
-
Và đây là code đươc tạo tự động
gh.factory<_i3.StorageService>(() => _i3.StorageService()); gh.factory<_i3.LanguageService>( () => _i3.LanguageService(gh<_i3.StorageService>()));
4. Đăng kí singleton
-
Nhắc lại : singleton là dạng đăng kí mà getIt chỉ tạo một instance duy nhất cho các đối tượng này, khi bạn access đến đối tượng nó sẽ trả về instance đã tạo, mà không tạo mới.
-
Bạn chỉ cần thêm annotation
@singleton
hoặclazysingleton
để đăng kí một class singleton. -
Một số sự thay thế tương đương :
-
getIt.registerSingleton(signalsReady)
»@Singleton(signalsReady: true)
-
@LazySingleton()
»getIt.registerLazySingleton(() => Model())
-
-
Ví dụ :
@singleton class AppInfo {}
-
Và đây là code được tạo tự động
gh.singleton<_i3.AppInfo>(_i3.AppInfo());
5. Huỷ singleton
-
GetIt
cung cấp cách huỷ singleton và lazysingleton-
Cách 1: Gắn annotation
@disposeMethod
vào một phương thức của class@singleton // or lazySingleton class DataSource { @disposeMethod void dispose(){ // logic to dispose instance } }
-
Cách 2 : Truyền hàm vào giá trị của annotation
```dart @Singleton(dispose: disposeDataSource) class DataSource { void dispose() { // logic to dispose instance } } /// dispose function signature must match Function(T instance) FutureOr disposeDataSource(DataSource instance){ instance.dispose(); }
```
-
Đây là code được tạo tự động
gh.singleton<_i4.DataSource>( _i4.DataSource(), dispose: (i) => i.dispose(), ); gh.singleton<_i4.DataSource2>( _i4.DataSource2(), dispose: _i4.disposeDataSource, );
-
6. FactoryMethod và PostConstruct
-
Mặc định khi đăng kí
@injectable
, injectable sẽ lấy contructor mặc định, nếu muốn dùng named contructor thì gắn annotation@factoryMethod
@injectable class LoginRepository { final RestApi restApi; LoginRepository(this.restApi); @factoryMethod LoginRepository.from(this.restApi); }
- Lúc này, khi generate code, injectable sẽ lấy named contructor
gh.factory<_i5.LoginRepository>( () => _i5.LoginRepository.from(gh<_i5.RestApi>()));
-
Annotation
@factoryMethod
cũng hỗ trợ tạo static method bên trong một abstract class.@injectable abstract class IAutheService { @factoryMethod static LoginRepository create(RestApi client) => LoginRepository(client); @factoryMethod factory IAutheService.from() => AutheService(); } class AutheService implements IAutheService {}
-
@PostConstruct
Annotation này hỗ trợ gọi một hàm ngay khi đối tượng được khởi tạo.@Injectable() class SomeController { SomeController(IAutheService service); @PostConstruct() void init() { //...init code } }
- Khi code được tạo tự động
gh.factory<_i7.SomeController>( () => _i7.SomeController(gh<_i5.IAutheService>())..init());
7. Đăng kí Async Injectable
-
Yêu cầu GetIt >= 4.0.0
-
Nếu bạn muốn tạo async factory, bạn cần khai báo một function static trả về một future.
-
Bây giờ chỉ cần chỉ định
@injectable
cho class, và@factoryMethod
cho method static khởi tạo@injectable class ApiClient { @factoryMethod static Future<ApiClient> create() async { return ApiClient(); } }
- Lúc này,
injectable
sẽ tạo ra một factoryAsync
gh.factoryAsync<_i3.ApiClient>(() => _i3.ApiClient.create());
- Lúc này,
8. Pre-Resolving
-
Hỗ trợ await đối tượng trong quá trình đăng kí, thay vì await lúc bạn sử dụng
@module abstract class RegisterModule { @preResolve Future<SharedPreferences> get prefs => SharedPreferences.getInstance(); }
-
Khi code được generate :
await gh.factoryAsync<_i7.SharedPreferences>( () => registerModule.prefs, preResolve: true, );
-
Annotate
@FactoryMethod
và@Injectable()
cũng hỗ trợ điều này@FactoryMethod(preResolve: true) @PostConstruct(preResolve: true)
9. Passing Parameters
-
Yêu cầu : GetIt >= 4.0.0
-
Truyền tham số vào đối tượng sử dụng annotation
@factoryParam
@injectable class BackendService { BackendService(@factoryParam String url); }
-
Code được generate như sau :
factoryParam<BackendService, String, dynamic>( (url, _) => BackendService(url), );
10. Binding abstract classes to implementations
-
Khi sử dụng dependency, chúng ta thường dùng interface truyền vào contructors
-
Dependency sẽ hỗ trợ resolve đối tượng tương ứng. Trong Injectable để làm được điều này chúng ta cần sử dụng
@Injectable(as: Service) class ServiceImpl implements Service {} // or @Singleton(as: Service) class ServiceImpl implements Service {} // or @LazySingleton(as: Service) class ServiceImpl implements Service {}
-
Ví dụ :
abstract class IFacebookLogin { Future<void> login(); } @Injectable(as: IFacebookLogin) class FacebookLogin implements IFacebookLogin { @override Future<void> login() async { // } }
-
Code sau khi được generate
gh.factory<_i7.IFacebookLogin>(() => _i7.FacebookLogin());
11. Binding mabstract classes to mulit implementations
-
Trường hợp abstract được implement trên nhiều class thì như thế nào ?
- Khi sử dụng làm sao biết lấy class nào để inject.
-
Injectable hỗ trợ annnotation
@Named
import 'package:injectable/injectable.dart'; abstract class ISocialLogin { Future<void> login(); } @Named('facebook') @Injectable(as: ISocialLogin) class FacebookLogin implements ISocialLogin { @override Future<void> login() async { // } } @Named('google') @Injectable(as: ISocialLogin) class GoogleLogin implements ISocialLogin { @override Future<void> login() async { // } } @injectable class LoginAuth { final ISocialLogin socialLogin; //Khi sử dụng cần xác định @Named LoginAuth(@Named('google') this.socialLogin); }
-
Khi code được generate
gh.factory<_i7.ISocialLogin>( () => _i7.FacebookLogin(), instanceName: 'facebook', ); gh.factory<_i7.ISocialLogin>( () => _i7.GoogleLogin(), instanceName: 'google', ); gh.factory<_i7.LoginAuth>( () => _i7.LoginAuth(gh<_i7.ISocialLogin>(instanceName: 'google')));
12. Auto Tagging
-
Khi sử dụng @Name bạn cần chuyển một chuỗi cứng vào như thế này :
@Named('facebook') @Named('google')
-
Trong khi class nó implement lại là như thế này :
class GoogleLogin implements ISocialLogin class FacebookLogin implements ISocialLogin
-
Khi sử dụng cần chỉ định chuỗi cứng đấy như vậy dễ truyền sai và không thống nhất. Để tránh điều này chúng ta có thể dùng tên của class implement luôn.
abstract class ISocialLogin { Future<void> login(); } @named @Injectable(as: ISocialLogin) class FacebookLogin implements ISocialLogin { @override Future<void> login() async { // } } @named @Injectable(as: ISocialLogin) class GoogleLogin implements ISocialLogin { @override Future<void> login() async { // } } @injectable class LoginAuth { final ISocialLogin socialLogin; LoginAuth(@Named.from(GoogleLogin) this.socialLogin); }
14. Register under different environments
- Annotation
@Environment('name')
hỗ trợ chỉ định class được generate theo từng môi trường.
15. Registering third party types
- TBD
16. Auto registering
-
Thay vì phải gắn annotation ở mỗi class bạn muốn inject. Bạn có thể chỉ định bất cứ class nào kết thúc với key cụ thể như Service, Repository hoặc Bloc
-
Để generate tự động, bạn tạo file build.yaml cùng thư mục với pubspec.yaml và thêm đoạn cấu hình sau :
targets: $default: builders: injectable_generator:injectable_builder: options: auto_register: true # auto registers any class with a name matches the given pattern class_name_pattern: "Service$|Repository$|Bloc$" # auto registers any class inside a file with a # name matches the given pattern file_name_pattern: "_service$|_repository$|_bloc$"
-
Manual Order : Mặc định injectable cố gắng đặt phụ thuộc dựa trên các phụ thuộc khác. Nghĩa là nếu A phụ thuộc B thì B sẽ được đăng kí đầu tiên.
-
Bạn có thể chỉ định thủ công thứ tự đăng kí thông qua thuộc tính
order
// @Order(-1) this works too @Injectable(order: -1) class Service{}
-
Using Scopes : GetIt 5.0 hỗ trợ scope, nghĩa là nó cho phép đăng kí trong phạm vi khác nhau. Vì vậy nó chỉ được khởi tạo khi cần và huỷ khi không cần nữa
-
Để dùng
GetIt
scope, gắn annotation@Scope('scope-name')
hoặc truyền vào tham số của@Injectable(scope: 'scope-name')
// @Scope('auth') this works too @Injectable(scope: 'auth') class AuthController{}
-
Khi bạn đã sẵn sàng dùng auth-scope, gọi method init hoặc extension
// using extensions getIt.initAuthScope(); // using methods initAuthScope(getIt); // scope-init method will return future if it has pre-resolved dependencies // so make sure you await it await getIt.initAuthScope();