Подробное изучение виджета ListView и его особенностей.
Некоторое время назад я написал статью об основах использования ListView и GridView во Flutter. Эта статья предназначена для более детального изучения класса ListView, ScrollPhysics, а также параметров конфигурирования и оптимизаций для общего виджета.
ListView во Flutter представляет собой линейный список прокручиваемых элементов. Мы можем использовать его для создания прокручиваемого списка или списка повторяющихся элементов.
Изучение типов ListView
Мы начнем с рассмотрения типов ListViews, а позже рассмотрим другие возможности и усовершенствования для него.
Рассмотрим типы ListViews:
ListView
ListView.builder
ListView.separated
ListView.custom
Давайте исследовать эти типы один за другим:
ListView
Это дефолтный конструктор класса ListView. ListView просто берет список дочерних элементов и делает из него список с возможностью прокрутки.
![Список, построенный с использованием конструктора по умолчанию Список, построенный с использованием конструктора по умолчанию](https://habrastorage.org/getpro/habr/upload_files/c75/7f1/eb3/c757f1eb303ab91315920d36c22da6aa.gif)
Общий формат кода:
ListView(
children: <Widget>[
ItemOne(),
ItemTwo(),
ItemThree(),
],
),
ListView.builder()
Конструктор builder()
строит повторяющийся список элементов. Конструктор принимает два основных параметра: itemCount для подсчета количества элементов в списке и itemBuilder конструктор для каждого построенного элемента списка.
![Список, построенный с помощью конструктора builder() Список, построенный с помощью конструктора builder()](https://habrastorage.org/getpro/habr/upload_files/0aa/c8b/a63/0aac8ba638df74d201c671b8b508619f.gif)
Общий формат кода:
ListView.builder(
itemCount: itemCount,
itemBuilder: (context, position) {
return listItem();
},
),
Хитрый трюк: так как они загружаются не сразу и только необходимое количество, нам на самом деле не нужно, чтобы item Count (счетчик элементов) был в качестве обязательного параметра, таким образом список может быть бесконечным.
ListView.builder(
itemBuilder: (context, position) {
return Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Text(position.toString(), style: TextStyle(fontSize: 22.0),),
),
);
},
),
![ListView без параметра ItemCount ListView без параметра ItemCount](https://habrastorage.org/getpro/habr/upload_files/cd0/f52/fe4/cd0f52fe432eba325069a0ae3a67d684.gif)
ListView.separated()
В конструкторе separated()
мы генерируем список и можем указать разделитель между каждым элементом.
![ListView, построенный с помощью конструктора ListView.separated() ListView, построенный с помощью конструктора ListView.separated()](https://habrastorage.org/getpro/habr/upload_files/11b/95c/285/11b95c285e86ecdc4660e625b39436fb.gif)
По сути, мы строим два переплетенных списка: один как основной, другой как разделительный.
Обратите внимание, что бесконечный счетчик, обсуждавшийся в предыдущем конструкторе, не может быть использован здесь. Этот конструктор принудительно приводит в действие счетчик элементов itemCount
.
Код для этого типа идет как:
ListView.separated(
itemBuilder: (context, position) {
return ListItem();
},
separatorBuilder: (context, position) {
return SeparatorItem();
},
itemCount: itemCount,
),
Этот тип списка позволяет динамически определять разделители, иметь различные типы разделителей для различных типов элементов, добавлять или удалять разделители по мере необходимости и т.д.
Данная реализация также может быть использована для вставки других типов элементов (например, рекламы) легко и без внесения каких-либо изменений в основной список в середине элементов списка.
Примечание: Длина списка разделителей на 1 единицу меньше, чем список элементов, так как после последнего элемента разделитель не ставится.
ListView.custom()
Конструктор custom()
, как подсказывает его название, позволяет создавать ListViews с с индивидуальными функциональными возможностями для построения дочерних элементов списка. Основным параметром, необходимым для этого, является SliverChildDelegate
, который формирует компоненты. Существуют следующие типы SliverChildDelegates
:
SliverChildListDelegate
SliverChildBuilderDelegate .
SliverChildListDelegate
принимает прямой дочерний список, в то время как SliverChildBuiderDelegate
принимает IndexedWidgetBuilder
(Функция сборщика, которую мы используем).
Вы можете использовать или разбить их на подклассы для создания собственных участников списка.
ListView.builder по сути является ListView.custom с функцией SliverChildBuilderDelegate.
Конструктор по умолчанию ListView ведет себя как ListView.custom с SliverChildListDelegate.
Теперь, когда мы закончили с типами ListViews, давайте взглянем на ScrollPhysics.
Изучение ScrollPhysics
Для управления прокруткой мы устанавливаем физический параметр в конструкторе ListView. Различные типы этого параметра:
NeverScrollableScrollPhysics .
NeverScrollScrollPhysics
запрещает прокрутку списка. Используйте это, чтобы полностью отключить прокрутку ListView.
BouncingScrollPhysics .
BouncingScrollPhysics
возвращает список обратно, когда список заканчивается. Аналогичный эффект используется на iOS.
![](https://habrastorage.org/getpro/habr/upload_files/49e/bac/2aa/49ebac2aaf0e7b05b99a385c34e8d3f6.gif)
ClamppingScrollPhysics
Это функция прокрутки, которая по умолчанию используется на Android. Список останавливается в конце и показывает индикацию этого действия.
FixedExtentScrollPhysics
Это немного отличается от других списков в том смысле, что работает только с FixedExtendScrollControllers
и списками, которые их используют. Для примера возьмём ListWheelScrollView
, который делает список, напоминающий по форме колесо.
FixedExtentScrollPhysics
только прокручивает элементы вместо любого смещения между ними.
![](https://habrastorage.org/getpro/habr/upload_files/84f/8f5/bbb/84f8f5bbb3a7b91e670ce865fd22ac4c.gif)
Код для этого примера невероятно прост:
FixedExtentScrollController fixedExtentScrollController =
new FixedExtentScrollController();
ListWheelScrollView(
controller: fixedExtentScrollController,
physics: FixedExtentScrollPhysics(),
children: monthsOfTheYear.map((month) {
return Card(
child: Row(
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
month,
style: TextStyle(fontSize: 18.0),
),
)),
],
));
}).toList(),
itemExtent: 60.0,
),
Ещё несколько вещей, которые нужно знать
Как сохранить элементы, которые удаляются при работе со списком?
Flutter позволяет использовать виджет KeepAlive(), сохраняющий элемент, который в противном случае был бы удален. В списке элементы по умолчанию встроены в виджет AutomaticKeepAlive
.
AutomaticKeepAlives
можно отключить, установив в поле addAutomaticKeepAlives
значение false
. Это полезно в тех случаях, когда элементы не нуждаются в сохранении или в пользовательской версии KeepAlive
.
Почему мой ListView имеет отступ между списком и внешним виджетом?
По умолчанию ListView имеет отступ между списком и внешним виджетом, чтобы удалить его, установите EdgeInsets.all(0.0).
Вот и все для этой статьи!
Надеюсь, вам понравилось.
Продолжайте следить за мной, чтобы узнать больше о Flutter, оставляйте свои мнения и комментарии.
Перевод статьи подготовлен в преддверии старта курса «Flutter Mobile Developer».
В преддверии старта курса приглашаем всех желающих записаться на бесплатны демо-урок по теме: «Графика во Flutter».
На уроке участники вместе с экспертом-ведущим разберут, как устроен рендеринг во Flutter и изучат основные компоненты библиотекиdart:ui
.