В библиотеке Flutter Provider представляет собой смесь Инъекции Зависимостей (Dependency Injection или DI) и управления состоянием, построенная с помощью виджетов и для виджетов.
Он использует Флаттер-виджеты для управления состоянием вместо Дарт классов, таких как Steam
. Причина в том, что виджеты очень просты, но надежны и масштабируемы.
Содержание статьи:
Используя виджеты для управления состоянием, провайдер может гарантировать:
- удобное управление кодом, благодаря принудительному однонаправленному потоку данных
- тестируемость и компонуемость, так как всегда можно переопределять значение
- надежность, так как тяжело забыть обработать сценарий обновления модели или виджета
Страница пакета pub.dev/packages/provider
Установка значения Flutter Provider #
Для установки значения переменной, используя провайдер, оберните один из виджетов в виджет Provider
(провайдер) и передайте ему свою переменную. После чего все потомки этого виджета смогу получить доступ к ней.
В качестве примера — можно обернуть все приложение в виджет Provider
и передать ему нашу переменную:
Provider<String>.value(
value: 'Hello World',
child: MaterialApp(
home: Home(),
)
)
Кроме того, для сложных объектов большинство провайдеров предоставляют конструктор, который принимает функцию для создания значения. Провайдер вызовет эту функцию только один раз, при вставке виджета в дерево, и предоставит результат. Это идеально подходит для отображения сложного объекта, который никогда не изменяется со временем (виджеты StatefulWidget
).
Следующий код создает и предоставляет класс MyComplexClass
. И в случае, когда провайдер удаляется из дерева виджетов, экземпляр MyComplexClass
будет удален.
Provider<MyComplexClass>(
create: (context) => MyComplexClass(),
dispose: (context, value) => value.dispose()
child: SomeWidget(),
)
Чтение значения установленной переменной Flutter Provider #
Простой способ чтения значения — это использование статического метода Provider.of<T>(BuildContext context)
.
Этот метод будет искать в дереве виджетов, начиная с виджета, связанного с переданным BuildContext
(родительский виджет), и он вернет ближайшую найденную переменную типа T
или вернет Throw
, если ничего не найдено.
В сочетании с первым примером, где мы установили значение, этот виджет будет читать наше значение (value) типа String
и отображать «Hello World»:
class Home extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Text(
/// Не забывайте передавать тип объекта, который вы хотите получить
Provider.of<String>(context)
);
}
}
В качестве альтерантивы Provider.of
, мы можем использовать Consumer
and Selector
.
Они могут быть полезны для оптимизации производительности, или же при трудностях получения BuildContext
провайдера.
MultiProvider
Используйте MultiProvider, когда хотите передать несколько значений ниже по дереву.
Dart Flutter
Когда вы передаете несколько значений, Flutter Provider может стать слишком нечитабельным, например:
Provider<Foo>(
create: (_) => Foo(),
child: Provider<Bar>(
create: (_) => Bar(),
child: Provider<Baz>(
create: (_) => Baz(),
child: someWidget,
)
)
)
В подобной ситуации, лучше использовать MultiProvider
, для повышения читаемости:
MultiProvider(
providers: [
Provider<Foo>(create: (_) => Foo()),
Provider<Bar>(create: (_) => Bar()),
Provider<Baz>(create: (_) => Baz()),
],
child: someWidget,
)
Поведение этих двух блоков кода совершенно одинаково, отличается только внешний вид кода.
ProxyProvider
Начиная с версии 3.0.0, появился новый вид провайдера: ProxyProvider
.
ProxyProvider — это провайдер, который объединяет несколько значений из других провайдеров в новый объект и отправляет результат провайдеру.
Этот новый объект будет обновляться всякий раз, когда один из провайдеров зависящий от обновлений (например ChangeNotifierProvider
).
В следующем примере Прокси-провайдер используется для построения переводов на основе счетчика, поступающего от другого провайдера:
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => Counter()),
ProxyProvider<Counter, Translations>(
create: (_, counter, __) => Translations(counter.value),
),
],
child: Foo(),
);
}
class Translations {
const Translations(this._value);
final int _value;
String get title => 'You clicked $_value times';
}
Consumer #
Иногда необходимо, чтобы только некоторые виджеты обновлялись, а не все вложенные. Для этого вместо Provider.of
используем Consumer
или Selector
.
Их необязательный аргумент child, позволяет перестраивать только часть дерева виджетов:
Foo(
child: Consumer<A>(
builder: (_, a, child) {
return Bar(a: a, child: child);
},
child: Baz(),
),
)
В этом примере только Bar
будет перестроен, когда обночиться A
. Но Foo
и Baz
не будут при этом перестраиваться.
Selector #
Чтобы продвинуться еще глубже, можно использовать Selector
для игнорирования изменений, если они не влияют на дерево виджетов:
Selector<List, int>(
selector: (_, list) => list.length,
builder: (_, length, __) {
return Text('$length');
}
);
Этот фрагмент будет перестроен только если длина списка изменится. Но не будет обновляться при обновлении элементов списка.
5 ответов к “Flutter Provider — объяснение работы”
Вау, очень доступно и понятно. Удивлен, что нет комментариев. Автор спасибо!
лучше почитайте официальный туториал пэкеджа, пользы будет больше
Вы правы, это перевод всего лишь части туториала, который вы упомянули.
Спасибо!!!!!!!
Спасибо!