Ответ
Да, я использовал различные подходы к внедрению зависимостей во 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 для кодогенерации на основе аннотаций.