fluttercommunity / get_it

Get It - Simple direct Service Locator that allows to decouple the interface from a concrete implementation and to access the concrete implementation from everywhere in your App. Maintainer: @escamoteur
https://pub.dev/packages/get_it
MIT License
1.34k stars 145 forks source link

[Question] How to use registerLazySingletonAsync with SharedPreferences #273

Closed ykaito21 closed 2 years ago

ykaito21 commented 2 years ago

I'm doing ResoCoder's TDD Clean Architecture Course, and it use get_it as DI. It was created in 2019, I tried to create the updated version, but I couldn't find the clear way to handle the case. Am I doing something wrong? Should I use registerSingletonWithDependencies or Is there any better way?

This is the original code: get_it: ^3.1.0

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await init();
  runApp(MyApp());
}

final sl = GetIt.instance;

Future<void> init() async {
  //! Features - Number Trivia
  // Bloc
  sl.registerFactory(
    () => NumberTriviaBloc(
      concrete: sl(),
      inputConverter: sl(),
      random: sl(),
    ),
  );

  // Use cases
  sl.registerLazySingleton(() => GetConcreteNumberTrivia(sl()));
  sl.registerLazySingleton(() => GetRandomNumberTrivia(sl()));

  // Repository
  sl.registerLazySingleton<NumberTriviaRepository>(
    () => NumberTriviaRepositoryImpl(
      localDataSource: sl(),
      networkInfo: sl(),
      remoteDataSource: sl(),
    ),
  );

  // Data sources
  sl.registerLazySingleton<NumberTriviaRemoteDataSource>(
    () => NumberTriviaRemoteDataSourceImpl(client: sl()),
  );

  sl.registerLazySingleton<NumberTriviaLocalDataSource>(
    () => NumberTriviaLocalDataSourceImpl(sharedPreferences: sl()),
  );

  //! Core
  sl.registerLazySingleton(() => InputConverter());
  sl.registerLazySingleton<NetworkInfo>(() => NetworkInfoImpl(sl()));

  //! External
  final sharedPreferences = await SharedPreferences.getInstance();
  sl.registerLazySingleton(() => sharedPreferences);
  sl.registerLazySingleton(() => http.Client());
  sl.registerLazySingleton(() => DataConnectionChecker());
}

This is my updated version: get_it: ^7.2.0

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  init();
  await sl.isReady<SharedPreferences>();
  runApp(const MyApp());
}

final sl = GetIt.instance;

void init() {
  //! Features - Number Trivia
  // Bloc
  sl.registerFactory(() => NumberTriviaBloc(
        getConcreteNumberTrivia: sl(),
        getRandomNumberTrivia: sl(),
        inputConverter: sl(),
      ));

  // Use cases
  sl.registerLazySingleton(() => GetConcreteNumberTrivia(sl()));
  sl.registerLazySingleton(() => GetRandomNumberTrivia(sl()));

  // Repository
  sl.registerLazySingleton<NumberTriviaRepository>(
      () => NumberTriviaRepositoryImpl(
            localDataSource: sl(),
            remoteDataSource: sl(),
            networkInfo: sl(),
          ));

  // Data sources
  sl.registerLazySingleton<NumberTriviaRemoteDataSource>(
      () => NumberTriviaRemoteDataSourceImpl(
            client: sl(),
          ));
  sl.registerLazySingleton<NumberTriviaLocalDataSource>(
      () => NumberTriviaLocalDataSourceImpl(
            sharedPreferences: sl(),
          ));

  //! Core
  sl.registerLazySingleton(() => InputConverter());
  sl.registerLazySingleton<NetworkInfo>(() => NetworkInfoImpl(sl()));

  //! External
  // maybe I don't need lazy just registerSingletonAsync
  sl.registerLazySingletonAsync(() async => SharedPreferences.getInstance());
  sl.registerLazySingleton(() => http.Client());
  sl.registerLazySingleton(() => InternetConnectionChecker());
}
escamoteur commented 2 years ago

When a non async Singleton depends on one with an async initialization registerSingletonWithDependencies is your way to go.

konstantins-v commented 2 years ago

@ykaito21 , you can create an AbstractCache and SharedPreferenceImpl

import 'package:shared_preferences/shared_preferences.dart';

abstract class AbstractCache {
  getStringList(String key);
  Future<bool> setStringList(String key, List<String> value);
}

class SharedPreferenceCache extends AbstractCache {

  SharedPreferences cache;

  SharedPreferenceCache({
    required this.cache
  });

  @override
  getStringList(String key) async {
    var inst =  await SharedPreferences.getInstance();

    return inst.getStringList(key);
  }

  @override
  Future<bool> setStringList(String key, List<String> value) async {
    var inst =  await SharedPreferences.getInstance();

    return inst.setStringList(key, value);
  }
}

After that use AbstractClass in NumberTriviaLocalDataSourceImpl.

So and fix your di/service_locator.dart like:

  final sharedPreferences = await SharedPreferences.getInstance();
  s1.registerLazySingleton<AbstractCache>(() => SharedPreferenceCache(cache: sharedPreferences));
swarajkumarsingh commented 1 year ago

Step 1: Register your dependency(shared preferences)

class DependencyInjection {
static void setup(){ 
 GetIt.instance.registerLazySingletonAsync<SharedPreferences>(
  () => SharedPreferences.getInstance(),
);
await GetIt.instance.isReady<SharedPreferences>(); // Add this line
GetIt.instance.registerLazySingleton<SharedDataService>(
  () => SharedDataServiceImp(
    GetIt.instance<SecureStorageService>(),
    GetIt.instance<SharedPreferences>(),
  ),
 );
}

Step 2: Attach the function to main func DependencyInjection.setup();

Step 3: Usage final sf = getIt();