Как работает dependency injection во Flutter
Что проверяют
Знаете ли вы, как зависимость попадает в виджет во Flutter и чем DI отличается от сервис-локатора. Следом обычно спрашивают, как подменить сервис в тестах и чем context.watch отличается от context.read.
Как отвечать
Основа всего это InheritedWidget: виджет выше по дереву хранит объект, а потомки находят его через BuildContext за константное время и могут подписаться на изменения. Если вы из веба, это близкий аналог React Context. Поверх него построены популярные подходы:
- конструктор: самый простой и честный вариант, зависимость передаётся параметром. Для сервисов и репозиториев часто лучший выбор;
provider: обёртка надInheritedWidget. Сервис без состояния отдают черезProvider, изменяемую модель черезChangeNotifierProvider, несколько сразу черезMultiProvider. Вbuildчитаютcontext.watch<T>(), в обработчиках нажатийcontext.read<T>();- Riverpod: провайдеры объявляются глобально, но состояние живёт в
ProviderScope, не зависит отBuildContext, а ошибки поиска ловятся при компиляции. Для тестов естьoverrides; get_it: реестрGetIt.IсregisterSingleton,registerLazySingletonиregisterFactory, часто вместе с генераторомinjectable. Строго говоря это сервис-локатор: класс сам достаёт зависимость, а не получает её снаружи.
Сильный ответ связывает выбор с архитектурой: репозитории и API-клиенты создаются один раз на старте и передаются вниз, а виджеты получают готовые объекты и не знают, как их собрать.
Где спотыкаются
Оборачивают обычный сервис в ChangeNotifierProvider. Он требует наследника ChangeNotifier, и такой код просто не скомпилируется. Сервису без состояния хватит Provider.
Ищут зависимость контекстом, который находится выше провайдера, например в том же build, где провайдер создан. Получают ProviderNotFoundException. Лечится отдельным виджетом или Builder.
Вызывают get_it в глубине бизнес-логики. Зависимости становятся невидимыми из сигнатуры, и в тестах приходится вспоминать, что зарегистрировать в глобальном реестре. Надёжнее доставать их в одной точке сборки и передавать через конструктор.