Провайдеры
Теперь, когда мы установили Riverpod, давайте поговорим о провайдерах.
Провайдеры - самая важная составляющая Riverpod приложения. Провайдер - объект, который инкапсулирует часть состояния и позволяет наблюдать за этим состоянием.
Зачем использовать провайдеры?
Инкапсуляция части состояния в провайдере:
Предоставляет легкий доступ к этому состоянию из разных мест. Провайдеры являются полной заменой таким паттернам, как Singleton, Service Locator, Dependency Injection и InheritedWidget.
Упрощает совместное использование данного состояния с другими. Пытались когда-нибудь объединить несколько объектов в один? Данный сценарий реализован внутри провайдеров.
Повышение производительности. Отслеживание перестроек виджетов или кэширование результатов затратных вычислений. Провайдеры гарантируют, что только изменение состояния вызывает перестойку/перевычисление.
Повышение тестируемости вашего приложения. С провайдерами вам больше не нужны
setUp
/tearDown
. Более того, можно изменить поведение любого провайдера во время теста, что позволяет легко протестировать конкретное поведение.Простое внедрение дополнительное функционала, как логгирование или pull-to-refresh.
Создание провайдера
Существует множество различных провайдеров, но все они работают одинаково.
Самый распространенный вариант использования это объявление провайдера в виде глобальной константы:
final myProvider = Provider((ref) {
return MyValue();
});
Не переживайте, что провайдеры создаются как глобальные переменные. Провайдеры полностью неизменяемы. Объявление провайдера ничем не отличается от объявления функции. Провайдеры поддаются тестированию и сопровождению.
Данный отрывок кода состоит из трех частей:
final myProvider
- объявление переменной. В дальнейшем мы будем использовать ее для чтения состояния нашего провайдера. Провайдеры всегда должны быть объявлены сfinal
.Provider
- тип провайдера, который мы решили использовать. Provider самый простой провайдер. Он предоставляет объект, который никогда не изменится. Мы можем заменить Provider другим провайдером, например StreamProvider или StateNotifierProvider, для иного взаимодействия со значением.Функция, которая создает состояние. Эта функция всегда получает
ref
в качестве параметра.ref
дает нам возможность читать другие провайдеры, выполнять какие-либо операции при аннулировании состояния нашего провайдера и т. п.
Тип возвращаемого значения данной функции определяется типом провайдера, который мы используем. Например, для Provider функция может создавать объект любого типа. Однако для StreamProvider функция должна возвращать Stream.
Вы можете объявить сколько угодно провайдеров без ограничения.
Riverpod, в отличие от package:provider
, позволяет создавать несколько
провайдеров, хранящих один и тот же тип:
final cityProvider = Provider((ref) => 'London');
final countryProvider = Provider((ref) => 'England');
Два провайдера со значением типа String
не вызовут ошибку.
Для работы с провайдерами мы должны добавить ProviderScope в корень вашего Flutter приложения:
void main() {
runApp(ProviderScope(child: MyApp()));
}
Разнообразие провайдеров
Существует множество разных провайдеров для различных случаев применения.
С таким большим набором провайдеров иногда бывает сложно определить, какой тип предпочтительней использовать. Чтобы выбрать подходящий провайдер, воспользуйтесь данной таблицей.
Тип Провайдера | Функция создания провайдера | Пример использования |
---|---|---|
Provider | Возвращает любой тип | Класс сервиса / вычисленное значение (отфильтрованный список) |
StateProvider | Возвращает любой тип | Условие фильтрации / простейший объект состояния |
FutureProvider | Возвращает Future любого типа | Результат обращения к API |
StreamProvider | Возвращает Stream любого типа | Stream результатов, полученных с API |
StateNotifierProvider | Возвращает подкласс StateNotifier | Сложный объект состояния, который можно изменить только через интерфейс |
ChangeNotifierProvider | Возвращает подкласс ChangeNotifier | Сложный объект состояния, который можно изменить напрямую |
Не смотря на то что каждый провайдер имеет свое предназначение, ChangeNotifierProvider не рекомендуется
использовать в масштабируемых приложениях из-за проблем с изменяемым состоянием.
Этот провайдер есть в пакете flutter_riverpod
для легкой миграции с package:provider
, также он необходим
для специфичных случаев, таких как взаимодействие с пакетами Navigator 2.
Модификаторы провайдера
Все провайдеры имеют встроенную возможность расширения функционала.
Можно добавить новые свойства к объекту ref
или же слегка изменить чтение провайдера.
Модификаторы можно использовать с любым провайдером. Синтаксис их использования
похож на именованный конструктор:
final myAutoDisposeProvider = StateProvider.autoDispose<int>((ref) => 0);
final myFamilyProvider = Provider.family<String, int>((ref, id) => '$id');
На данный момент существует лишь два модификатора:
.autoDispose
, который автоматически аннулирует состояние провайдера, если он больше никем не прослушивается..family
, который позволяет создать провайдер с использованием дополнительных параметров.
Можно применить сразу несколько модификаторов к одному провайдеру:
final userProvider = FutureProvider.autoDispose.family<User, int>((ref, userId) async {
return fetchUser(userId);
});
На этом заканчивается данный гайд!
Также вы можете изучить Как читать провайдер. Посмотрите еще Как комбинировать провайдеры.