Ответ
Да, я использовал различные подходы к внедрению зависимостей во Flutter для создания тестируемого и слабосвязанного кода.
1. Использование пакета get_it (Service Locator):
Применял в проектах, где нужен был простой и глобальный доступ к сервисам.
// Настройка локатора (обычно в main.dart или отдельном файле)
final getIt = GetIt.instance;
void setupDependencies() {
// Регистрируем синглтон (один экземпляр на всё приложение)
getIt.registerSingleton<ApiService>(ApiService());
// Регистрируем фабрику (новый экземпляр при каждом запросе)
getIt.registerFactory<LoginBloc>(() => LoginBloc(getIt<ApiService>()));
// Регистрируем ленивый синглтон (создается при первом запросе)
getIt.registerLazySingleton<SharedPreferencesService>(
() => SharedPreferencesService(),
);
}
// Использование в коде
class MyRepository {
final ApiService _api = getIt<ApiService>(); // Внедрение через локатор
Future<Data> fetchData() => _api.getData();
}
2. Использование provider/riverpod (управление состоянием с DI):
Эти пакеты не только управляют состоянием, но и естественным образом решают задачу DI через контекст.
// Настройка провайдеров с Riverpod
final apiServiceProvider = Provider<ApiService>((ref) => ApiService());
final repositoryProvider = Provider<MyRepository>(
(ref) => MyRepository(ref.watch(apiServiceProvider)),
);
// Использование в виджете
class MyWidget extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final repository = ref.watch(repositoryProvider); // Зависимость получена
// ...
}
}
Почему это важно:
- Тестируемость: Легко подменить реальный
ApiServiceна мок в тестах. - Слабая связность: Классы не создают свои зависимости жестко, а получают их извне.
- Управление жизненным циклом: DI-контейнер (
get_itилиriverpod) контролирует, когда создавать и пересоздавать экземпляры (синглтон, фабрика).
В сложных проектах также рассматривал решения вроде injectable для кодогенерации на основе аннотаций.
Ответ 18+ 🔞
А, ну это про внедрение зависимостей во Flutter! Да, сталкивался, конечно. Ёпта, без этого сейчас никуда — если хочешь код, который не развалится от одного чиха и который можно нормально потестировать, без DI как без рук, честно говоря.
Первый подход — через get_it, это типа сервис-локатор.
Использовал, когда нужно было, чтобы сервисы были доступны отовсюду, как вода в кране. Просто, быстро, но, блядь, доверия к такому подходу иногда ноль — слишком глобально, можно нагородить говна.
// Настраиваешь эту штуку где-нибудь при старте (в main.dart или отдельно)
final getIt = GetIt.instance;
void setupDependencies() {
// Регистрируешь синглтон — один экземпляр на всё приложение, как батя на районе
getIt.registerSingleton<ApiService>(ApiService());
// Фабрика — новый экземпляр каждый раз, когда просишь. Свежак, так сказать.
getIt.registerFactory<LoginBloc>(() => LoginBloc(getIt<ApiService>()));
// Ленивый синглтон — создаётся только когда впервые попросят. Хитрая жопа, экономит ресурсы.
getIt.registerLazySingleton<SharedPreferencesService>(
() => SharedPreferencesService(),
);
}
// А потом в любом месте кода просто берёшь, что надо
class MyRepository {
final ApiService _api = getIt<ApiService>(); // Вот тебе и внедрение, ёб твою мать
Future<Data> fetchData() => _api.getData();
}
Второй способ — через provider или riverpod.
Эти ребята, они не только за состояние отвечают, но и за зависимости заодно. Очень удобно, всё через контекст течёт. Особенно riverpod — там вообще красота, волнение ебать, как хорошо.
// Объявляешь провайдеры с Riverpod
final apiServiceProvider = Provider<ApiService>((ref) => ApiService());
final repositoryProvider = Provider<MyRepository>(
(ref) => MyRepository(ref.watch(apiServiceProvider)), // Смотри, как зависимость прокидывается
);
// В виджете используешь
class MyWidget extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final repository = ref.watch(repositoryProvider); // Всё, репозиторий с нужным сервисом внутри
// Дальше работаешь...
}
}
А зачем весь этот цирк, спросишь? Да похуй, шучу. На самом деле, причины овердохуища:
- Тестируемость: Хочешь потестить логику? Без проблем! В тестах подменяешь реальный
ApiServiceна заглушку-мок, и всё, ты король. Никаких проблем с сетью или базой. - Слабая связность: Классы перестают быть упрямыми ослами и сами себе ничего не создают. Они получают готовое извне. Если что-то сломалось — ищешь в одном месте, а не по всему коду.
- Управление жизненным циклом: Контейнер (будь то
get_itили провайдер) решает, когда объект родить, а когда убить. Синглтон живёт вечно, фабрика — одноразовая, ленивый — пока не попросят. Красота!
В особо навороченных проектах ещё и injectable применял — это когда кодогенерация на основе анноций делает за тебя всю грязную работу. Выглядит как магия, но, блядь, работает.