предисловие
Мне очень жаль, проект в последнее время слишком загружен, поэтому обновление идет слишком медленно. Без дальнейших церемоний, давайте начнем.
Selector
Чтение документов
Вообще-то я не собирался говоритьSelector
Да, но друг хочет, чтобы я представил его, так что начни сSelector
Начинать.
В основном,Selector
а такжеConsumer
эквивалентно, такжеProvider.of
Отличие получения данных в том, что,Selector
Как следует из его имени, он будет отфильтровывать ненужное обновление данных, чтобы предотвратить перестроение, то естьSelector
Будут обновлены только подходящие данные.
Давайте сначала посмотримSelector
Определение:
class Selector<A, S> extends Selector0<S> {
/// {@macro provider.selector}
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<A, S>
Дженерики в:
-
A
это то, что мы получаем от верхнего уровняProvider
тип -
S
Это конкретный вид заботы, то есть получается.Provider
Типы, которые действительно полезны для нас, должны быть вselector
возвращает этот тип в . этоSelector
Диапазон обновления также из всегоProvider
стал С.
бросить быстрый взглядSelector
свойства в:
- селектор: это
Function
, выйдя на верхний уровень получимprovider
передать, а затем вернуть то, что нам небезразличноS
. -
shouldRebuild
: это свойство будет сохраненоselector
Отфильтрованное значение, т.е.selector
вернутьS
и взять новый после получения уведомленияS
с кэшемS
сравнивать, судить об этомSelector
Нужно ли перестраивать, по умолчаниюpreview!=next
просто обновите, если этоcollection
,selector
Сравнение глубины. - строитель: и
Consumer
То же самое, здесь возвращается элемент управления, который нужно построить, второй параметрprovider
, что мы только чтоselector
вернулся вS
. - дочерний: Это обновление для оптимизации некоторой неиспользуемой части, прежде чем мы скажем
Consumer
также сказал в то время.
по умолчанию,Selector
серединаbuilder
Будет ли вызвано обновление, зависит отselector
Сравните результат между старыми и новыми данными, если старые и новые данныеcollection
, то этот результат сравнения получаетсяcollection
в упаковкеDeepCollectionEqualityРезультат.
Это поведение по умолчанию может быть настроеноshouldRebuild
Обратный вызов на переписать.
Примечание. Выбранные данные должны быть неизменяемыми, иначе
Selector
Можно подумать, что ничего не изменилось, поэтому застройщика больше вызывать не будут.
так,selector
Должен вернуть коллекцию (List/Map/Set/Iterable) или переопределить==
тип.
Но иногда мы не хотим переписать==
, самый простой способ добиться того же эффекта — использоватьTuple:
Selector<Foo, Tuple2<Bar, Baz>>(
selector: (_, foo) => Tuple2(foo.bar, foo.baz),
builder: (_, data, __) {
return Text('${data.item1} ${data.item2}');
}
)
В приведенном выше примере толькоfoo.bar
илиfoo.bar
когда происходят изменения,builder
будет вызван снова.
Подключитесь на то, как использовать конкретные, вы можете узнать самостоятельно.
Например
То, что я сказал выше, не более чем список официальных документов, поговорим о конкретных приложениях.
Кратко о функции, которую мы хотим реализовать.Она очень проста.Есть список товаров.При нажатии на товар,товар добавится в корзину. Эта функция на самом деле очень проста, нам нужноCommodity
Установить, добавляется ли поле в корзинуisSelected
, затем, когда мы нажимаем на элемент, мы хотим обновитьisSelected
Поле, то мы, безусловно, заметимFlutter
Обновите пользовательский интерфейс, если используетеChangeNotifier
, то есть звонитьnotifyListeners
. Это может удовлетворить наши потребности, но если хорошо подумать, если использовать этот способ, то все зависимости, которые зависят от этого ProviderCommodity
Он будет обновлен, и весь список будет обновлен, это действительно необходимо?
В настоящее время мы можем рассмотреть возможность использованияSelector
Сделайте оптимизацию - отфильтровать ненужные обновления.
Во-первых, мы создаемCommodityProvider
:
class CommodityProvider with ChangeNotifier {
List<Commodity> _commodityList =
List.generate(10, (index) => Commodity('Commodity Name_$index', false));
get commodityList => _commodityList;
get length => commodityList.length;
addToCart(int index) {
Commodity commodity = commodityList[index];
commodityList[index] = Commodity(commodity.name, !commodity.isSelected);
notifyListeners();
}
}
Commodity
Этот класс сущности очень прост, всего два поля, одно из которых — название продукта.name
, другой - пометить, добавлено ли в корзинуisSelected
. в_commodityList
В реальной работе он вообще получается с сервера.Здесь мы его прямо пишем до смерти для удобства. а такжеaddToCart
Метод заключается в добавлении или удалении из корзины, при нажатии на соответствующийindex
, мы добавим этот товар в корзину или удалим его из нее. мы проходим черезcommodityList
для отображения всего списка, в то время какlength
длина списка элементов.
Далее мы станем свидетелямиSelector
Реально ли обновить фильтр.
Далее нам еще предстоит пройти на страницу верхнего уровняChangeNotifierProvider
предоставить данные.
class CommodityListScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_)=>CommodityProvider(),
child: ourWidget,
);
}
}
Очевидно, что для реализации этого списка мы должны знать длину спискаlength
,а такжеlength
Он работает со всем списком, но мы не хотим, чтобы он обновлялся из-за изменения определенного элемента в списке, поэтому теперь нам нужно отфильтровать все обновления,Selector
Реализуйте «Потребителя», который не обновляется.
Selector<CommodityProvider, CommodityProvider>(
shouldRebuild: (pre, next) => false,
selector: (_, provider) => provider,
builder: (context, provider, child) {
print("build selector 1");
return ourWidget;
},
),
это здесь,Selector
Дженерики вA
а такжеS
обеCommodityProvider
, потому что мы хотим получить всюCommodityProvider
, но мы ставимshouldRebuild
Переписано, чтобы избежать ненужных обновлений.
Далее мы реализуем наш список продуктов:
ListView.builder(
itemCount: provider.length,
itemBuilder: (BuildContext context, int index) =>
Selector<CommodityProvider, Commodity>(
selector:
(BuildContext context, CommodityProvider provider) =>
provider.commodityList[index],
builder: (BuildContext context, Commodity commodity,
Widget child) {
print("build item $index");
return ListTile(
onTap: () => provider.addToCart(index),
title: Text("${commodity.name}"),
trailing: Icon(commodity.isSelected
? Icons.remove_shopping_cart
: Icons.add_shopping_cart),
);
},
));
}
мы можем видеть здесьselector
вернулсяprovider.commodityList[index]
, то есть конкретный товар, поэтому каждый товар должен заботиться только о своей трети земли, что нормально.Selector
Область обновления ограничена текущим элементом, пока мы находимся вSelector<CommodityProvider, Commodity>
изbuilder
Добавлен журнал для проверки механизма обновления фильтра.
Давай, запусти его, кликни по нескольким пунктам, а потом посмотри лог:
I/flutter (29438): build selector 1
I/flutter (29438): build item 0
I/flutter (29438): build item 1
I/flutter (29438): build item 2
I/flutter (29438): build item 3
I/flutter (29438): build item 4
I/flutter (29438): build item 5
I/flutter (29438): build item 6
I/flutter (29438): build item 7
I/flutter (29438): build item 8
I/flutter (29438): build item 9
I/flutter (29438): build item 7
I/flutter (29438): build item 5
I/flutter (29438): build item 4
как насчет этого? Теперь, когда мы обновляем только те элементы, по которым щелкнули, избегая обновления всего списка, мы на небольшой шаг приблизились к оптимизации производительности.
Если вы хотите узнать будущее, пожалуйста, послушайте следующую разбивку
так какProvider
Третья статья в серии, содержание по-прежнему очень простое, и я должен сказать, что время ограничено.
Продолжение следует. . . Ожидать или не ожидать, что последнее слово будет за вами.