Nguồn :

  • [get_it Dart Package](https://pub.dev/packages/get_it)
  • https://viblo.asia/p/flutter-dependency-injection-di-that-don-gian-voi-get-it-va-injectable-naQZRLkP5vx
    1. Giới thiệu
    • Package này hỗ trợ triển khai Dependency injection (DI)

    • InheritedWidget

      • Chúng ta có thể dùng widget này để triển khi DI. Nhưng nó có một số hạn chế :

        • Phải gắn trực tiếp vào UI

        • Dùng nhiều dependency thì bị lồng nhiều cấp.

        • Muốn truy cập một instance cần phải có context.

    • Get_it ra đời để giải quyết các hạn chế trên.

  1. Cài đặt

    • Cách cài đặt giống các package khác nên mình không mô tả thêm.
  2. Tạo các Instances

    • Factory : mỗi lần sử dụng các đối tượng sẽ tạo instance mới

      // Dùng khi instance khởi tạo được ngay lập tức.
      getIt.registerFactory<HomeBloc>(() => HomeBloc());
      
      // Dùng khi instance cần chạy bất đồng bộ để khởi tạo.
      getIt.registerFactoryAsync<HomeBloc>(() => HomeBloc.createAsync());
      
    • Singleton : Chỉ tạo một instance duy nhất

      // Dùng khi instance có thể khởi tạo được ngay
      getIt.registerSingleton<CounterRepository>(CounterRepository());
      
      // Dùng khi instance bắt buộc phải tạo dưới dạng bất đồng bộ (asynchronous)
      // hàm createAsync của CounterRepository trả về một Future<CounterRepository>
      getIt.registerSingletonAsync<CounterRepository>(() => CounterRepository.createAsync());
      
    • Lazy-singleton : Giống Singleton nhưng chỉ khởi tạo khi nó được dùng

      getIt.registerLazySingleton<CounterRepository>(() => CounterRepository());
      
  3. Sử dụng

    • getIt.get<T>() : T là instance cần dùng.

    • getIt.getAsync<T>() : Dùng cho trường hợp bất đồng bộ

  4. Giải quyết Dependency

    • A phụ thuộc B

      class A {
          final B b;
          
          const A(this.b);
      }
      
      class B {
      
      }
      
      
      // đăng kí B trước tiên
      getIt.registerSingleton<B>(B());
      
      getIt.registerFactory<A>(() {
          // lấy object B bên trên...
          final b = getIt.get<B>();
          
          // ...truyền vào constructor của A
          return A(b);
      });
      
      
    • A phụ thuôc async B

      • Lúc này phải chuyển contructor của A thành async, cụ thể :

        • registerFactoryAsync thay vì registerFactory

        • registerSingletonAsync thay vì registerSingleton

      class A {
          final B b;
          
          const A(this.b);
      }
      
      class B {
          static Future<B> createAsync() {
              // ... khởi tạo B
          }
      }
      
      
      // đăng kí B trước tiên
      getIt.registerSingletonAsync<B>(() => B.createAsync());
      
      getIt.registerFactoryAsync<A>(() async {
          // chờ và lấy object B bên trên...
          final b = await getIt.getAsync<B>();
          
          // ...truyền vào constructor của A
          return A(b);
      });
      
      
    • Async A phụ thuộc B

      class A {
          final B b;
          
          const A(this.b);
          
          static Future<A> createAsync(B b) {
              // ... khởi tạo A
          }
      }
      
      class B {
      
      }
      
      
      // đăng kí B trước tiên
      getIt.registerSingleton<B>(B())
      
      getIt.registerFactoryAsync<A>(() async {
          // lấy object B bên trên...
          final b = getIt.get<B>();
          
          // ...truyền vào hàm khởi tạo của A
          return await A.createAsync(b);
      });
      
    • Async A phụ thuộc Async B :

      class A {
          final B b;
          
          const A(this.b);
          
          static Future<A> createAsync(B b) {
              // ... khởi tạo A
          }
      }
      
      class B {
          static Future<B> createAsync() {
              // ... khởi tạo B
          }
      }
      
      // đăng kí B trước tiên
      getIt.registerSingletonAsync<B>(() => B.createAsync())
      
      getIt.registerFactoryAsync<A>(() async {
          // lấy object B bên trên...
          final b = await getIt.getAsync<B>();
      
          // ...truyền vào hàm khởi tạo của A
          return await A.createAsync(b);
      });
      
      
  5. Truyền params vào factory

    • get_it hỗ trợ truyền tối đa 2 params

      class User {
          final int age;
          final String name;
          
          const User({this.age, this.name});
      }
      
      // Khởi tạo
      getIt.registerFactoryParam<User, int, String>((age, name) => User(age: age, name: name));
      
      getIt.get<User>(param1: 5, param2: 'Kevin');
      
      • Nếu bạn muốn truyền nhiều hơn 2 params, có thể tạo một class đại diện cho các params và truyền vào như một param bình thường:
      class UserParams {
          final int age;
          final String name;
          final String address;
          
          const UserParams({this.age, this.name, this.address});
      }
      
      class User {
          final int age;
          final String name;
          final String address;
        
          const User({this.age, this.name, this.address});
        
          User.withParams(UserParams params) : this(age: params.age, name: params.name, address: params.address);
      }
      
      
      // Khởi tạo
      getIt.registerFactoryParam<User, UserParams>((params) => User.withParams(params));
      
      
      // Sử dụng
      getIt.get<User>(param1: UserParams(age: 5, name: 'Kevin', address: 'Hanoi'));
      
      
  6. Tự động đăng kí dependency với injectable

    • Hỗ trợ tự jnject thay vì phải tự khai báo

    • Cài đặt :

      dependencies:
          ...
          injectable: ^1.0.4
      
      dev_dependencies:
          ...
          injectable_generator: ^1.0.4
          build_runner: ^1.10.2
      
      
      • Bây giờ hãy mở file injection.dart, sửa lại thành như sau:
      import 'package:get_it/get_it.dart';
      import 'package:injectable/injectable.dart';
      
      import 'injection.config.dart';
      
      final getIt = GetIt.instance;
      
      @InjectableInit()
      Future<void> configureDependencies() async => await $initGetIt(getIt);
      
      
      • Bạn sẽ thấy báo lỗi ở import 'injection.config.dart'; và $initGetIt.
      flutter packages pub run build_runner build
      
      • Nó sẽ tạo ra một file mơi : injection.g.dart tạo bởi injectable nằm cùng vị trí với injection.dart
    • Sử dụng :

      • Giờ thì thay vì chúng ta viết mọi thứ ở trong configureDependencies(),hãy tạm quên nó đi và chuyển qua object bạn cần khởi tạo. Giả sử mình có 2 class A và B, mình muốn thêm nó vào DI dưới dạng factory, A phụ thuộc vào B thì mình chỉ cần import và thêm annotation @injectable phía trên 2 class đó
      import 'package:injectable/injectable.dart';
      
      @injectable
      class A {
          final B b;
          
          const A(this.b);
      }
      
      @injectable
      class B {
      
      }
      
      
    • Thực hiện tạo file

      // GENERATED CODE - DO NOT MODIFY BY HAND
      
      // **************************************************************************
      // InjectableConfigGenerator
      // **************************************************************************
      
      import 'package:get_it/get_it.dart';
      import 'package:injectable/injectable.dart';
      
      import 'models.dart';
      
      /// adds generated dependencies
      /// to the provided [GetIt] instance
      
      GetIt $initGetIt(
        GetIt get, {
        String environment,
        EnvironmentFilter environmentFilter,
      }) {
        final gh = GetItHelper(get, environment, environmentFilter);
        gh.factory<B>(() => B());
        gh.factory<A>(() => A(get<B>()));
        return get;
      }