Это 86-я оригинальная статья без воды.Если вы хотите получить больше оригинальных статей, выполните поиск в общедоступном аккаунте и подпишитесь на нас~ Эта статья была впервые опубликована в блоге Zhengcaiyun:[Flutter Skills] Поставщик управления состоянием, о котором вы должны знать
предисловие
Provider, компонент управления состоянием страницы Flutter, официально рекомендованный Google, его сущность на самом деле является упаковкой InheritedWidgets, что упрощает их использование и повторное использование. Я не буду слишком много рассказывать об InheritedWidget.В этой статье в основном представлено связанное с этим использование Provider более всесторонним образом, который можно использовать в бизнес-сценариях.
Пример исходного кода этой статьи:GitHub.com/Сам Сяомань…
Применение
Шаг 1: Добавьте зависимости
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
provider: ^4.0.4
Шаг 2: Соблюдайте структуру
воплощать в жизньflutter pub get
После этого в проекте можно увидеть исходный код sdk провайдера, структура такая:
Шаг 3: Знакомство с примером
В этом примере объясняется использование базовых компонентов Provider, включая, помимо прочего, ChangeNotifier, NotifierProvider, Consumer, Selector, ProxyProvider, FutureProvider, StreamProvider.
Шаг 4: Создайте уведомление об изменении
Сначала мы создаем новую модель Model1, наследуем ChangeNotifier и делаем ее одним из наших поставщиков данных.
class Model1 extends ChangeNotifier {
int _count = 1;
int get count => _count;
set count(int value) {
_count = value;
notifyListeners();
}
}
Отслеживая исходный код ChangeProvider, мы обнаружили, что он не принадлежит провайдеру, это фактически файл change_provider.dart, определенный в рамках Flutter SDK. ChangeNotifier реализует абстрактный класс Listenable, который поддерживает ObserverList. Исходный код класса для прослушивания:
abstract class Listenable {
const Listenable();
factory Listenable.merge(List<Listenable> listenables) = _MergingListenable;
void addListener(VoidCallback listener);
void removeListener(VoidCallback listener);
}
Видно, что в основном есть два метода addListener и removeListener. Исходный код класса ChangeNotifier:
class ChangeNotifier implements Listenable {
ObserverList<VoidCallback> _listeners = ObserverList<VoidCallback>();
bool _debugAssertNotDisposed() {
assert(() {
if (_listeners == null) {
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('A $runtimeType was used after being disposed.'),
ErrorDescription('Once you have called dispose() on a $runtimeType, it can no longer be used.')
]);
}
return true;
}());
return true;
}
@protected
bool get hasListeners {
assert(_debugAssertNotDisposed());
return _listeners.isNotEmpty;
}
void addListener(VoidCallback listener) {
assert(_debugAssertNotDisposed());
_listeners.add(listener);
}
@override
void removeListener(VoidCallback listener) {
assert(_debugAssertNotDisposed());
_listeners.remove(listener);
}
@mustCallSuper
void dispose() {
assert(_debugAssertNotDisposed());
_listeners = null;
}
@protected
@visibleForTesting
void notifyListeners() {
assert(_debugAssertNotDisposed());
if (_listeners != null) {
final List<VoidCallback> localListeners = List<VoidCallback>.from(_listeners);
for (VoidCallback listener in localListeners) {
try {
if (_listeners.contains(listener))
listener();
} catch (exception, stack) {
FlutterError.reportError(FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'foundation library',
context: ErrorDescription('while dispatching notifications for $runtimeType'),
informationCollector: () sync* {
yield DiagnosticsProperty<ChangeNotifier>(
'The $runtimeType sending notification was',
this,
style: DiagnosticsTreeStyle.errorProperty,
);
},
));
}
}
}
}
}
В дополнение к реализации addListener и removeListener также предоставляются два метода dispose и notifyListeners. В Model1, когда мы изменяем значение счетчика, вызывается метод notifyListeners, чтобы уведомить пользовательский интерфейс об обновлении.
Шаг 5: Создайте ChangeNotifierProvider
Образец введения
Способ 1: через ChangeNotifierProvider
return ChangeNotifierProvider(
create: (context) {
return Model1();
},
child: MaterialApp(
theme: ArchSampleTheme.theme,
home: SingleStatsView(),
),
);
Здесь ChangeNotifier (т.е. Model1) устанавливается посредством создания ChangeNotifierProvider. Областью действия является MaterialApp, указанный дочерним элементом. Здесь мы используем SingleStatsView в качестве домашней страницы, а Model1 используется в SingleStatsView в качестве источника данных. Следует отметить, что не устанавливайте область действия всех состояний в MaterialApp и строго контролируйте область действия в соответствии с реальными бизнес-требованиями, поскольку слишком большое количество глобальных состояний серьезно повлияет на производительность приложения.
class SingleStatsView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FlatButton(
color: Colors.blue,
child: Text('Model1 count++'),
onPressed: () {
Provider.of<Model1>(context, listen: false).count++;
},
),
Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: Text('Model count值变化监听',
style: Theme.of(context).textTheme.title),
),
Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: Text('Model1 count:${Provider.of<Model1>(context).count}',
style: Theme.of(context)
.textTheme
.subhead
.copyWith(color: Colors.green)),
),
],
),
);
}
}
Способ 2: через ChangeNotifierProvider.value
return ChangeNotifierProvider.value(
value: Model1(),
child: MaterialApp(
theme: ArchSampleTheme.theme,
home: SingleStatsView(),
));
Видно, что это почти то же самое, что и метод ChangeNotifier, созданный create в первом методе, создается значением, используемым здесь. Проследите исходный код ChangeNotifierProvider:
class ChangeNotifierProvider<T extends ChangeNotifier> extends ListenableProvider<T> {
static void _dispose(BuildContext context, ChangeNotifier notifier) {
notifier?.dispose();
}
ChangeNotifierProvider({
Key key,
@required Create<T> create,
bool lazy,
Widget child,
}) : super(
key: key,
create: create,
dispose: _dispose,
lazy: lazy,
child: child,
);
ChangeNotifierProvider.value({
Key key,
@required T value,
Widget child,
}) : super.value(
key: key,
value: value,
child: child,
);
}
Шаг 6: Отслеживайте изменения состояния страницы, другие методы использования
Пример
Давайте сначала разберемся с ValueListenableBuilder, который может отслеживать изменения в указанных значениях для обновлений пользовательского интерфейса, Использование выглядит следующим образом:
ValueNotifier
1. Новый ValueNotifier
final ValueNotifier<int> _counter = ValueNotifier<int>(0);
Назначьте его счетчику Model1 в методе построителя, чтобы _counter также мог прослушивать, когда счетчик в Model1 изменяется.
_counter.value = Provider.of<Model1>(context).count;
2. Свяжите ValueListenableBuilder valueListenable из ValueListenableBuilder может привязать ValueNotifier для отслеживания изменения значения ValueNotifier.
ValueListenableBuilder(
valueListenable: _counter,
builder: (context, count, child) => Text(
'ValueListenableBuilder count:$count'),
),
Исходный код ValueNotifier:
class ValueNotifier<T> extends ChangeNotifier implements ValueListenable<T> {
ValueNotifier(this._value);
@override
T get value => _value;
T _value;
set value(T newValue) {
if (_value == newValue)
return;
_value = newValue;
notifyListeners();
}
@override
String toString() => '${describeIdentity(this)}($value)';
}
Видно, что ValueNotifer также наследует ChangeNotifier и реализует ValueListenable.Особенностью является то, что notifyListeners вызывается при установке значения, тем самым реализуя мониторинг изменения состояния.
MultiProvider
Образец введения
Если бизнес-сценарий сложный, нашей странице может потребоваться отслеживать несколько источников данных ChangeNotifier, и здесь MultiProvider пригодится. Этот пример расширен на SingleStatsView, здесь мы создаем новый MultiStatsView для отслеживания изменений данных Model1 и Model2.
Model2
class Model2 extends ChangeNotifier {
int _count = 1;
int get count => _count;
set count(int value) {
_count = value;
notifyListeners();
}
}
MultiStatsView
class MultiStatsView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FlatButton(
color: Colors.blue,
child: Text('Model1 count++'),
onPressed: () {
Provider.of<Model1>(context, listen: false).count++;
},
),
FlatButton(
color: Colors.blue,
child: Text('Model2 count++'),
onPressed: () {
Provider.of<Model2>(context, listen: false).count++;
},
),
Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: Text('Model count值变化监听',
style: Theme.of(context).textTheme.title),
),
Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: Text('Model1 count:${Provider.of<Model1>(context).count}',
style: Theme.of(context)
.textTheme
.subhead
.copyWith(color: Colors.green)),
),
Padding(
padding: const EdgeInsets.only(bottom: 24.0),
child: Text('Model2 count:${Provider.of<Model2>(context).count}',
style: Theme.of(context)
.textTheme
.subhead
.copyWith(color: Colors.red)),
),
],
),
);
}
}
Используя MultiProvider для связи с MultiStatsView, мы видим, что MultiProvider предоставляет массив провайдеров, мы можем поместить в него ChangeNotifierProvider.
return MultiProvider(
providers: [
ChangeNotifierProvider.value(value: Model1()),
ChangeNotifierProvider.value(value: Model2()),
],
child: MaterialApp(
theme: ArchSampleTheme.theme,
localizationsDelegates: [
ArchSampleLocalizationsDelegate(),
ProviderLocalizationsDelegate(),
],
home: MultiStatsView(),
),
);
Если для MultiStatsView предоставляется только ChangeNotifierProvider, связанный с Model1, вы увидите следующую ошибку:
══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
The following ProviderNotFoundException was thrown building MultiStatsView(dirty, dependencies:
[_LocalizationsScope-[GlobalKey#48c61], _InheritedTheme, _DefaultInheritedProviderScope<Model1>]):
Error: Could not find the correct Provider<Model2> above this MultiStatsView Widget
To fix, please:
* Ensure the Provider<Model2> is an ancestor to this MultiStatsView Widget
* Provide types to Provider<Model2>
* Provide types to Consumer<Model2>
* Provide types to Provider.of<Model2>()
* Ensure the correct `context` is being used.
Это потому, что Model2 не зарегистрирована.
ProxyProvider
Начиная с 3.0.0 доступноProxyProvider
.ProxyProvider
Вы можете агрегировать значения других провайдеров в новый объект и передать результат вProvider
. Новые объекты обновляются при обновлении хостинг-провайдера, от которого они зависят.
Создайте новый класс пользователя
class User {
var name;
User(this.name);
}
Пропускаем Model1 черезProxyProvider
Агрегируйте в объект пользователя, а затем возьмите объект пользователяname
рендерить. с
ProxyProvider<Model1, User>(
update: (context, value, previous) => User("change value ${value.count}"),
builder: (context, child) => Text(
'ProxyProvider: ${Provider.of<User>(context).name}',
style: Theme.of(context).textTheme.title),
)
Видно, что черезProxyProvider
кстати, мы напрямую звонимProvider.of<User>(context)
ценность, ассоциацияUser
Провайдер у нас не зарегистрирован, но тоже работает эффективно.
FutureProvider
Как видно из названия, этот Provider связан с асинхронным выполнением, и его использование аналогичноFutureBuilder
. использовать здесьFutureProvider
Обновите начальное значение Model1 после 2 секунд симуляции. допустимыйinitialData
Укажите начальное значение, метод создания указывает конкретную асинхронную задачу, которую можно использовать в методе построителя.Provider.of
Извлеките значение, возвращаемое асинхронным выполнением задачи для отрисовки страницы. также может определитьcatchError
поймать исключение,updateShouldNotify
Сравнивая старые и новые значения для перестроения, новые callback-функции создания/обновления загружаются отложенно, то есть они вызываются при первом чтении соответствующего значения, а не при первом создании провайдера. Если вам не нужна эта функция, вы можете использоватьlazy
Значение свойства установлено равным false.
FutureProvider<Model1>(
create: (context) {
return Future.delayed(Duration(seconds: 2))
.then((value) => Model1()..count = 11);
},
initialData: Model1(),
builder: (context, child) => Text(
'FutureProvider ${Provider.of<Model1>(context).count}',
style: Theme.of(context).textTheme.title),
),
StreamProvider
Это видно из названия.StreamProvider
Это также поставщик, связанный с асинхронным выполнением, и его использование аналогично StreamBuilder. использовать здесьStreamProvider
Симуляция обновляет начальное значение Model1 каждую 1 секунду. остальные параметры иFutureProvider
Использование аналогично.
StreamProvider(create: (context) {
return Stream.periodic(Duration(seconds: 1), (data) => Model1()..count = data);
},
initialData: Model1(),
builder: (context, child) => Text(
'StreamProvider: ${Provider.of<Model1>(context).count}',
style: Theme.of(context).textTheme.title),
),
Consumer
Конкретное использование выглядит следующим образом: параметры в построителе: контекст контекста, значение T, дочерний элемент виджета, значение — Model1,Тип значения такой же, как Model1, метод построителя возвращает виджет, то есть виджет, обернутый потребителем.При изменении значения отслеживаемой модели виджет будет перестроен.
Consumer<Model1>(
builder: (context, model, child) {
return Text('Model1 count:${model.count}');
},
)
Selector
Видно, что Selector и Consumer очень похожи, единственное отличие состоит в том, что Selector может настраивать тип возвращаемого значения, как следует из Selector, здесь мы слушаем изменение счетчика в Model1, поэтому тип возвращаемого значения здесь определяется как тип Int. Параметры в методе построителя: контекст контекста, значение T, дочерний элемент виджета,Тип значения здесь такой же, как тип возвращаемого значения, определенный в Selector.. Метод построителя возвращает виджет, который представляет собой виджет, обернутый селектором.Мы можем указать отслеживать изменение значения в ChangeNotifier, что может вызвать перестроение виджета.
Selector<Model1, int>(
builder: (context, count, child) => Text(
"Selector示例演示: $count",
style: Theme.of(context).textTheme.title,
),
selector: (context, model) => model.count,
),
Исходный код:
class Selector<A, S> extends Selector0<S> {
Selector({
Key key,
@required ValueWidgetBuilder<S> builder,
@required S Function(BuildContext, A) selector,
ShouldRebuild<S> shouldRebuild,
Widget child,
}) : assert(selector != null),
super(
key: key,
shouldRebuild: shouldRebuild,
builder: builder,
selector: (context) => selector(context, Provider.of(context)),
child: child,
);
}
Видно, что Selector наследуется от Selector0, трассирует исходный код Selector0 и создает виджет через buildWithChild.
class _Selector0State<T> extends SingleChildState<Selector0<T>> {
T value;
Widget cache;
Widget oldWidget;
@override
Widget buildWithChild(BuildContext context, Widget child) {
final selected = widget.selector(context);
var shouldInvalidateCache = oldWidget != widget ||
(widget._shouldRebuild != null && widget._shouldRebuild.call(value, selected)) ||
(widget._shouldRebuild == null && !const DeepCollectionEquality().equals(value, selected));
if (shouldInvalidateCache) {
value = selected;
oldWidget = widget;
cache = widget.builder(
context,
selected,
child,
);
}
return cache;
}
}
Здесь A, S, видно, что A — входной параметр функции-селектора, а S — возвращаемое значение функции Здесь A преобразуется в Provider через Provider.of(context). Сравните приведенный выше пример с селектором, где A соответствует Model1, а S соответствует количеству. Также есть функция shouldRebuild, посмотрите определение функции:
typedef ShouldRebuild<T> = bool Function(T previous, T next);
Сравнивая предыдущее значение и текущее значение, принимается решение о перестроении страницы.Если оно возвращает true, страница будет перерисовываться один раз, а если возвращает false, страница перерисовываться не будет. Конкретную логику оценки см. в методе buildWithChild класса _Selector0State.
Видно, что по сравнению с Consumer, Selector сужает область мониторинга данных и может настраивать необходимость обновления страницы в соответствии со своей собственной бизнес-логикой, тем самым избегая множества ненужных обновлений страницы, тем самым повышая производительность.
В файле selector.dart структуры кода Provider SDK видно, что также определены Selector2, Selector3, Selector4, Selector5 и Selector6. Вот пример Selector2, поясняющий его использование:
class Selector2<A, B, S> extends Selector0<S> {
Selector2({
Key key,
@required ValueWidgetBuilder<S> builder,
@required S Function(BuildContext, A, B) selector,
ShouldRebuild<S> shouldRebuild,
Widget child,
}) : assert(selector != null),
super(
key: key,
shouldRebuild: shouldRebuild,
builder: builder,
selector: (context) => selector(
context,
Provider.of(context),
Provider.of(context),
),
child: child,
);
}
Видно, что Selector2 также наследует Selector0, разница в том, что функция selector имеет два входных параметра A и B, а S — возвращаемое значение функции. То есть вы можете отслеживать 2 провайдера через Selector2, и вы можете настроить мониторинг изменения значения S от этих 2 провайдеров. Другие Селекторы просто слушают больше Провайдеров. Если необходимо отслеживать более 6 провайдеров, необходимо настроить метод Selector. В этом примере мы используем Selector2 для одновременного отслеживания изменений в Model1 и Model2 и добавляем счетчик в две модели.
Selector2<Model1, Model2, int>(
selector: (context, model1, model2) {
return model1.count + model2.count;
},
builder: (context, totalCount, child) {
return Text(
'Model1和Model2 count合计:$totalCount');
}
Selector3, Selector4... и Consumer2, Consumer3..., здесь я не буду вдаваться в подробности.
Проверка производительности потребителей и селекторов
После приведенных выше примеров мы уже кое-что знаем о Consumer и Selector. Потребитель может избежать избыточной перестройки виджетов.Если значение, отслеживаемое в Потребителе, не изменяется, обернутые виджеты не будут перестроены. Selector обеспечивает более точный мониторинг на основе Consumer, а также поддерживает пользовательскую перестройку, которая позволяет более гибко управлять проблемами перестроения виджетов.
Давайте вместе проверим ситуацию с перестроением Consumer и Selector. На иллюстрации Step3 мы определяем две кнопки, одну для накопления счетчика в Model1 и одну для накопления счетчика в Model2; одновременно демонстрируя использование Selector2 и Consumer; определяя Widget1, Widget2, Widget4 с Selector, Используется для проверки ситуация с восстановлением. Значение счетчика Model1 отмечено зеленым цветом, а значение счетчика Model2 — красным.
Пример исходного кода этой статьи:GitHub.com/Сам Сяомань…
Widget1, напечатайте «Widget1 build» в методе сборки.
class Widget1 extends StatefulWidget {
@override
State<StatefulWidget> createState() => StateWidget1();
}
class StateWidget1 extends State<Widget1> {
@override
Widget build(BuildContext context) {
print('Widget1 build');
return Text('Widget1', style: Theme.of(context).textTheme.subhead);
}
}
Widget2, напечатайте "Widget2 build" в методе сборки. Widget3, прослушивая изменения счетчика Model2, печатает «Widget3 build» в методе компоновщика.
Widget4, напечатайте «сборка Widget4» в методе сборки, метод сборки возвращает селектор, напечатайте «сборка селектора Widget4» в методе построителя селектора, а селектор прослушивает изменения в счетчике Model1.
class Widget4 extends StatefulWidget {
@override
State<StatefulWidget> createState() => StateWidget4();
}
class StateWidget4 extends State<Widget4> {
@override
Widget build(BuildContext context) {
print('Widget4 build');
return Selector<Model1, int>(
builder: (context, count, child) {
print('Widget4 Selector build');
return Text('Widget4 Model1 count:$count', style: Theme.of(context).textTheme.subhead.copyWith(color: Colors.green),);
},
selector: (context, model) => model.count,
);
}
}
Все условия соблюдены, запускаем страницу StatsView, лог печатает вот так:
Selector2 build
Model1 Consumer build
Model2 Consumer build
Widget1 build
Widget2 build
Widget3 build
Widget4 build
Widget4 Selector build
Видно, что порядок отрисовки виджета начинается сверху вниз в соответствии с порядком расположения элементов страницы. Нажмите кнопку подсчета Model1++, вы увидите все зеленые метки, где количество было обновлено. Журнал печатается следующим образом:
Selector2 build
Model1 Consumer build
Model2 Consumer build
Widget1 build
Widget2 build
Widget3 build
Widget4 build
Widget4 Selector build
Какой! ! ! Как обновить счетчик Model1, как при первой загрузке страницы, разве он не должен создавать и отслеживать только виджеты, связанные с Model1? Давайте изменим код, чтобы использовать Widget4 в качестве глобальной переменной и инициализировать его во время initState.
class StateStatsView extends State<StatsView> {
final ValueNotifier<int> _counter = ValueNotifier<int>(0);
var widget4;
@override
void initState() {
widget4 = Widget4();
super.initState();
}
@override
Widget build(BuildContext context) {
_counter.value = Provider.of<Model1>(context).count;
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
..., //此处省略部分实例源码
Widget1(),
Widget2(),
Widget3(),
widget4,
],
),
);
}
}
После запуска снова нажмите кнопку Model1 count++, и журнал распечатается следующим образом:
Selector2 build
Model1 Consumer build
Model2 Consumer build
Widget1 build
Widget2 build
Widget3 build
Widget4 Selector build
Как видите, логи для "сборки Widget4" больше не печатаются, но логи для "сборки Widget4 Selector" все еще печатаются. Давайте снова изменим код и используем селектор в Widget4 как глобальную переменную, которая инициализируется во время initState.
class Widget4 extends StatefulWidget {
@override
State<StatefulWidget> createState() => StateWidget4();
}
class StateWidget4 extends State<Widget4> {
Selector<Model1, int> selector;
@override
void initState() {
selector = buildSelector();
super.initState();
}
@override
Widget build(BuildContext context) {
print('Widget4 build');
return selector;
}
Selector<Model1, int> buildSelector() {
return Selector<Model1, int>(
builder: (context, count, child) {
print('Widget4 Selector build');
return Text('Widget4 Model1 count:$count', style: Theme.of(context).textTheme.subhead.copyWith(color: Colors.green),);
},
selector: (context, model) => model.count,
);
}
}
После запуска снова нажмите кнопку Model1 count++, и журнал распечатается следующим образом:
Selector2 build
Model1 Consumer build
Model2 Consumer build
Widget1 build
Widget2 build
Widget3 build
Видно, что журнал «Сборка Widget4 Selector» не печатается, и счетчик в Model1, отслеживаемый в Widget4, также обновляется нормально. Проверив предыдущие 3 шага, мы можем узнать, чтоКогда ChangeNotifier (здесь Model1) уведомляет об обновлении (notifyListener), виджет в области действия Model1 запускает сборку, селектор и потребитель по сути являются виджетом.Когда наши данные должны быть упакованы селектором или потребителем, он рекомендуется initState.Сначала создайте виджет, чтобы избежать ненужных сборок.
разное
1.listen
Если мы изменим кнопку «Model1 count++», нажмите код события на
FlatButton(
color: Colors.blue,
child: Text('Model1 count++'),
onPressed: () {
Provider.of<Model1>(context).count++;
},
),
Разница в том, что без listen:false нажмите кнопку, и вы увидите следующий журнал ошибок:
Tried to listen to a value exposed with provider, from outside of the widget tree.
This is likely caused by an event handler (like a button's onPressed) that called
Provider.of without passing `listen: false`.
To fix, write:
Provider.of<Model1>(context, listen: false);
It is unsupported because may pointlessly rebuild the widget associated to the
event handler, when the widget tree doesn't care about the value.
Официальная страница комментариев объясняет listen, listen=true, что означает, что значение в отслеживаемом ChangeNotifier изменяется, и соответствующий виджет будет перестроен, а если listen=false, то не будет перестроен. Чтобы вызвать метод Provider.of вне дерева виджетов, нужно добавить listen: false.
2. Расширение
Провайдер поддерживает методы расширения с версии 4.1.0.Текущий пример основан на 4.0.5+1.Я не буду здесь вдаваться в подробности.Подробнее см.Changelog.
before | after |
---|---|
Provider.of(context, listen: false) | context.read() |
Provider.of(context) | context.watch |
Другие компоненты управления состоянием
компоненты | представлять |
---|---|
Provider | Официальная рекомендация, основанная на реализации InheritedWidget. |
ScopedModel | на основе реализации InheritedWidget иProviderПринцип и написание очень похожи |
BLoC | Основанный на реализации Stream, этот режим требует определенного понимания реактивного программирования (например, RxDart, RxJava). Основная концепция: входные событияSink<Event> input , выходное событиеStream<Data> output
|
Redux | Реализация Flutter пакета Redux в экологической цепочке React в веб-разработке популярна во внешнем интерфейсе, архитектуре одностороннего потока данных. Основная концепция: объекты храненияStore , операция событияAction , обработка и распределение событийReducer , обновление компонентаView
|
Mobx | Первоначально это была библиотека управления состоянием JavaScript, но позже она была перенесена в версию dart. Основная идея:Observables ,Actions ,Reactions
|
Остальные компоненты здесь повторяться не будут, читатели, которым это интересно, могут изучить и понять принципы реализации других компонентов.
Суммировать
В этой статье в основном представлены официальные рекомендации по использованиюProviderКомпонент в сочетании с исходным кодом и проблемами, возникающими в обычном процессе разработки бизнеса, представляет несколько распространенных методов использования, я надеюсь, что каждый сможет использовать его умело и гибко использовать в бизнес-сценариях.
Рекомендуемое чтение
В сочетании с исходным кодом React вам потребуется пять минут, чтобы освоить приоритетную очередь.
Написание высококачественного поддерживаемого кода: абстракция компонентов и гранулярность
Карьера
ZooTeam, молодая, увлеченная и творческая команда, связанная с отделом исследований и разработок продукции Zhengcaiyun, базируется в живописном Ханчжоу. В настоящее время в команде более 40 фронтенд-партнеров, средний возраст которых составляет 27 лет, и почти 30% из них — инженеры полного стека, настоящая молодежная штурмовая группа. В состав членов входят «ветераны» солдат из Ali и NetEase, а также первокурсники из Чжэцзянского университета, Университета науки и технологий Китая, Университета Хандянь и других школ. В дополнение к ежедневным деловым связям, команда также проводит технические исследования и фактические боевые действия в области системы материалов, инженерной платформы, строительной платформы, производительности, облачных приложений, анализа и визуализации данных, а также продвигает и внедряет ряд внутренних технологий. Откройте для себя новые горизонты передовых технологических систем.
Если вы хотите измениться, вас забрасывают вещами, и вы надеетесь начать их бросать; если вы хотите измениться, вам сказали, что вам нужно больше идей, но вы не можете сломать игру; если вы хотите изменить , у вас есть возможность добиться этого результата, но вы не нужны; если вы хотите изменить то, чего хотите достичь, вам нужна команда для поддержки, но вам некуда вести людей; если вы хотите изменить установившийся ритм, это будет "5 лет рабочего времени и 3 года стажа работы"; если вы хотите изменить исходный Понимание хорошее, но всегда есть размытие того слоя оконной бумаги.. , Если вы верите в силу веры, верьте, что обычные люди могут достичь необыкновенных вещей, и верьте, что они могут встретить лучшего себя. Если вы хотите участвовать в процессе становления бизнеса и лично способствовать росту фронтенд-команды с глубоким пониманием бизнеса, надежной технической системой, технологиями, создающими ценность, и побочным влиянием, я думаю, что мы должны говорить. В любое время, ожидая, пока вы что-нибудь напишете, отправьте это наZooTeam@cai-inc.com