Подробное изучение виджета ListView и его особенностей.

Некоторое время назад я написал статью об основах использования ListView и GridView во Flutter. Эта статья предназначена для более детального изучения класса ListView, ScrollPhysics, а также  параметров конфигурирования и оптимизаций для общего виджета.

ListView во Flutter представляет собой линейный список прокручиваемых элементов. Мы можем использовать его для создания прокручиваемого списка или списка повторяющихся элементов.

Изучение типов ListView

Мы начнем с рассмотрения типов ListViews, а позже рассмотрим другие возможности и усовершенствования для него.

Рассмотрим типы ListViews:

  1. ListView

  2. ListView.builder

  3. ListView.separated

  4. ListView.custom

Давайте исследовать эти типы один за другим:

ListView

Это дефолтный конструктор класса ListView. ListView просто берет список дочерних элементов и делает из него список с возможностью прокрутки.

Список, построенный с использованием конструктора по умолчанию

Общий формат кода:

ListView(
  children: <Widget>[
    ItemOne(),
    ItemTwo(),
    ItemThree(),
  ],
),

ListView.builder()

Конструктор builder() строит повторяющийся список элементов. Конструктор принимает два основных параметра: itemCount для подсчета количества элементов в списке и  itemBuilder конструктор для каждого построенного элемента списка.

Список, построенный с помощью конструктора builder()

Общий формат кода:

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.separated()

В конструкторе separated() мы генерируем список и можем указать разделитель между каждым элементом.

ListView, построенный с помощью конструктора ListView.separated()

По сути, мы строим два переплетенных списка: один как основной, другой как разделительный.

Обратите внимание, что бесконечный счетчик, обсуждавшийся в предыдущем конструкторе, не может быть использован здесь. Этот конструктор принудительно приводит в действие счетчик элементов itemCount.

Код для этого типа идет как:

ListView.separated(
      itemBuilder: (context, position) {
        return ListItem();
      },
      separatorBuilder: (context, position) {
        return SeparatorItem();
      },
      itemCount: itemCount,
),

Этот тип списка позволяет динамически определять разделители, иметь различные типы разделителей для различных типов элементов, добавлять или удалять разделители по мере необходимости и т.д.

Данная реализация также может быть использована для вставки других типов элементов (например, рекламы) легко и без внесения каких-либо изменений в основной список в середине элементов списка.

Пример показывает рекламу, когда Position делится на 4

Примечание: Длина списка разделителей на 1 единицу меньше, чем список элементов, так как после последнего элемента разделитель не ставится.

ListView.custom()

Конструктор custom(), как подсказывает его название, позволяет создавать ListViews с с индивидуальными функциональными возможностями для построения дочерних элементов списка. Основным параметром, необходимым для этого, является SliverChildDelegate, который формирует компоненты. Существуют следующие типы SliverChildDelegates :

  1. SliverChildListDelegate

  2. SliverChildBuilderDelegate .

SliverChildListDelegate принимает прямой дочерний список, в то время как SliverChildBuiderDelegate принимает IndexedWidgetBuilder (Функция сборщика, которую мы используем).

Вы можете использовать или разбить их на подклассы для создания собственных участников списка.

ListView.builder по сути является ListView.custom с функцией SliverChildBuilderDelegate.


Конструктор по умолчанию ListView ведет себя как ListView.custom с SliverChildListDelegate.

Теперь, когда мы закончили с типами ListViews, давайте взглянем на ScrollPhysics.

Изучение ScrollPhysics

Для управления прокруткой мы устанавливаем физический параметр в конструкторе ListView. Различные типы этого параметра:

NeverScrollableScrollPhysics .

NeverScrollScrollPhysics запрещает прокрутку списка. Используйте это, чтобы полностью отключить прокрутку ListView.

BouncingScrollPhysics .

BouncingScrollPhysics возвращает список обратно, когда список заканчивается. Аналогичный эффект используется на iOS.

ClamppingScrollPhysics

Это функция прокрутки, которая по умолчанию используется на Android. Список останавливается в конце и показывает индикацию этого действия.

FixedExtentScrollPhysics

Это немного отличается от других списков в том смысле, что работает только с FixedExtendScrollControllers и списками, которые их используют. Для примера возьмём ListWheelScrollView, который делает список, напоминающий по форме колесо.

FixedExtentScrollPhysics только прокручивает элементы вместо любого смещения между ними.

Код для этого примера невероятно прост:

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.