Ответ
Dependency Injection — это паттерн, при котором зависимости объекта предоставляются извне, а не создаются внутри класса. В моих Flutter-проектах это критически важно для тестируемости и поддержки кода.
Практическая реализация в Dart/Flutter:
-
Конструкторный DI (наиболее чистый подход):
class UserRepository { final ApiService apiService; final LocalStorage storage; UserRepository({ required this.apiService, required this.storage, }); }
// Использование final repository = UserRepository( apiService: ApiService(baseUrl: 'https://api.example.com'), storage: HiveStorage(), );
2. **Использование пакетов для сложных случаев:**
- **GetIt** — сервис-локатор
- **Riverpod** — провайдер с встроенным DI
- **Injectable** — кодогенерация для DI
**Пример с GetIt:**
```dart
// setup_locator.dart
final getIt = GetIt.instance;
void setupDependencies() {
getIt.registerLazySingleton<ApiService>(() => ApiService());
getIt.registerFactory<UserRepository>(
() => UserRepository(apiService: getIt<ApiService>()),
);
}
// В приложении
void main() {
setupDependencies();
runApp(MyApp());
}
// В виджете
class UserProfile extends StatelessWidget {
final UserRepository repo = getIt<UserRepository>();
@override
Widget build(BuildContext context) {
// использование repo
}
}
Преимущества из моего опыта:
- Тестирование: легко подменять реальные сервисы моками
test('UserRepository test', () { final mockApi = MockApiService(); final repo = UserRepository(apiService: mockApi, storage: mockStorage); // тестируем изолированно }); - Гибкость: изменение реализации без правки потребителей
- Чистая архитектура: соблюдение принципа инверсии зависимостей (DIP)
В Flutter-контексте DI особенно важен для управления состоянием и сервисами, которые должны быть доступны во всем приложении.