Настройка Dependency Injection (GetIt) во Flutter-приложении
GetIt — service locator для Dart/Flutter, де-факто стандарт для DI в проектах, где не нужна кодогенерация. Принцип простой: регистрируешь зависимости один раз при старте приложения, запрашиваешь в любом месте через GetIt.instance<T>() или сокращение sl<T>().
Почему именно GetIt, а не что-то другое
provider и Riverpod — это State Management с DI как побочным эффектом. GetIt — чистый service locator без Flutter-зависимостей: его можно использовать в domain-слое без BuildContext. Для Clean Architecture, где domain-слой ничего не знает о Flutter, это принципиально.
Конструктор GetIt поддерживает три режима регистрации:
-
registerSingleton<T>— создаёт сразу при регистрации, живёт всё время работы приложения -
registerLazySingleton<T>— создаёт при первом обращении, потом возвращает тот же инстанс -
registerFactory<T>— создаёт новый инстанс при каждом обращении
На практике: ApiService, DatabaseHelper, SharedPreferences-обёртка — registerLazySingleton. ViewModel или BLoC, если их создаёт GetIt (редко) — registerFactory. Чаще BLoC создаётся через BlocProvider, а GetIt держит только инфраструктурный слой.
Типичная ошибка при инициализации
// Неправильно — синхронная регистрация асинхронной зависимости
sl.registerLazySingleton<DatabaseHelper>(() => DatabaseHelper()..init());
// Правильно — async init через registerSingletonAsync
sl.registerSingletonAsync<DatabaseHelper>(() async {
final db = DatabaseHelper();
await db.init();
return db;
});
// И дождаться готовности перед runApp:
await sl.allReady();
Если не использовать registerSingletonAsync + allReady(), DatabaseHelper может быть запрошен до завершения асинхронной инициализации — крэш на старте с StateError: Singleton is not ready yet.
Организация injection_container.dart
Стандартная практика — один файл injection_container.dart (или di/) с функцией initDependencies():
Future<void> initDependencies() async {
// External
final sharedPrefs = await SharedPreferences.getInstance();
sl.registerLazySingleton(() => sharedPrefs);
sl.registerLazySingleton(() => http.Client());
// Data sources
sl.registerLazySingleton<AuthRemoteDataSource>(
() => AuthRemoteDataSourceImpl(sl()),
);
// Repositories
sl.registerLazySingleton<AuthRepository>(
() => AuthRepositoryImpl(sl()),
);
// Use cases
sl.registerLazySingleton(() => LoginUseCase(sl()));
// BLoCs — если создаются через GetIt
sl.registerFactory(() => AuthBloc(loginUseCase: sl()));
}
Регистрируем в порядке зависимостей снизу вверх: сначала внешние зависимости, потом data sources, репозитории, use cases, наконец — presentation слой.
GetIt + flutter_modular или feature-based DI
На крупных проектах один injection_container.dart превращается в 500 строк. Решение: разбить по фичам. Каждый feature-модуль регистрирует свои зависимости через отдельную функцию initAuthDependencies(), initProfileDependencies() — вызываются из главного initDependencies().
Что делаем в рамках настройки
Анализируем текущую архитектуру проекта → определяем слои и зависимости → настраиваем injection_container с корректными временами жизни → прописываем async-инициализацию для БД и Storage → покрываем регистрацию unit-тестами через mock-подмену в тестовом setUp.
Работа занимает 1–2 дня в зависимости от размера проекта и количества существующих зависимостей.







