ListView
Прокручиваемый список. Есть две формы:
ListView
— дети перечислены прямо в разметке, и
ListView.builder
— строки строятся по шаблону-макросу
@Macros id:itemBuilder
, обычно из данных. Для длинных и динамических списков берите
ListView.builder
.
Тип:
multi-child
(
ListView
),
multi-child
с макросом-шаблоном (
ListView.builder
)
SizedBox height:200
ListView
Text "Первая строка"
Text "Вторая строка"
Text "Третья строка"
Список, как и любой скроллер, должен получать ограниченную высоту (или ширину при горизонтальной прокрутке): внутри Column оберните его в Expanded , либо задайте размер через SizedBox . Подробнее — в Bad practices.
Параметры
Общие для обеих форм:
| Параметр | Тип | Default | Описание |
|---|---|---|---|
scrollDirection
|
string | vertical |
Направление:
vertical
,
horizontal
|
reverse
|
bool | false | Начинать с конца (первая строка — внизу или справа) |
shrinkWrap
|
bool | false | Занимать место по содержимому, а не всё доступное |
physics
|
string | always |
Реакция на жест:
never
(не прокручивать),
bouncing
,
clamp
,
always
.
never
отдаёт колесо скроллеру снаружи — нужен вложенному списку
|
itemExtent
|
double | — | Фиксированный размер строки по главной оси — быстрее, чем считать каждую |
clipBehavior
|
string | hardEdge | Обрезка по границам списка |
keyboardDismissBehavior
|
string | manual |
onDrag
— убирать экранную клавиатуру при протаскивании списка
|
Только у
ListView.builder
:
| Параметр | Тип | Default | Описание |
|---|---|---|---|
itemCount
|
int | 0 | Сколько строк строить |
keepVisible
|
int | — | Индекс строки, которую список держит на экране: при его изменении список прокручивается к ней минимально необходимым образом |
Вложенные структуры
#padding
Отступ внутри области прокрутки: содержимое отступает от краёв, но прокручивается до самого края.
SizedBox height:160
ListView
#padding symmetric horizontal:16 vertical:8
Text "Строка 1"
Text "Строка 2"
Text "Строка 3"
Дочерние элементы
В
ListView
дети — сами строки.
В
ListView.builder
ребёнок один — макрос
@Macros id:itemBuilder
. Его тело — шаблон строки, а параметр
index
(в объявлении пишется
index:0
) получает номер строки при каждой сборке. Второй макрос с
id:separatorBuilder
превращает список в «со разделителями»: он ставится
между
строками (
itemCount - 1
раз), но не после последней.
SizedBox height:240
ListView.builder itemCount:20
@Macros id:itemBuilder index:0
Padding
#padding all 12
Text $"Строка ${index}"
@Macros id:separatorBuilder index:0
Divider
Список из данных
Строки обычно берут из генератора данных. Макрос получает только
index
; поля достаются индексом на месте вызова, всегда с
??
— пока данные не загрузились, значение
null
:
JsonData src:"data/products.json" data:@products count:@count
SizedBox height:320
ListView.builder itemCount:$count
@Macros id:itemBuilder index:0
Padding
#padding all 12
Text {products[index]['title'] ?? ""}
Фильтрацию и сортировку выполняют генераторами (
FilterData
) до списка, а не скрытием строк внутри шаблона: скрытые строки ломают счётчики и прокрутку.
Примеры
Горизонтальная лента
SizedBox height:96
ListView scrollDirection:horizontal
SizedBox width:120
ColoredBox color:#4f46e5
SizedBox width:120
ColoredBox color:#7c3aed
SizedBox width:120
ColoredBox color:#059669
Список под шапкой
Column
Padding
#padding all 16
Text "Заголовок"
Expanded
ListView.builder itemCount:50
@Macros id:itemBuilder index:0
Padding
#padding symmetric horizontal:16 vertical:8
Text $"Элемент ${index}"
Выбранная строка остаётся на экране
@sel:0
Column
SizedBox height:200
ListView.builder itemCount:30 keepVisible:$sel
@Macros id:itemBuilder index:0
Text $"Строка ${index}"
Button onPressed:{sel = sel + 1}
Text "Дальше"
Различия между приложением и сайтом
Сайт превращает список в HTML-список с прокруткой, и не все параметры у него работают:
ListView
|
ListView.builder
|
|
|---|---|---|
scrollDirection
|
работает | не работает — всегда вертикально |
#padding
|
работает | не работает |
itemExtent
|
работает | не работает |
reverse
,
shrinkWrap
,
physics
,
clipBehavior
|
не работают | не работают |
keyboardDismissBehavior
|
у страницы нет своей экранной клавиатуры — не имеет смысла | то же |
keepVisible
|
— | не работает |
Для горизонтальной ленты на сайте берите статический
ListView scrollDirection:horizontal
. Строки
ListView.builder
на сайте попадают в страницу все сразу, поэтому не стройте списком на тысячи строк то, что должно читаться поисковиком как страница.
Bad practices
Список без ограниченной высоты
// BAD — Column даёт ListView бесконечную высоту: в приложении это ошибка раскладки
Column
Text "Заголовок"
ListView
Text "Строка 1"
Text "Строка 2"
// GOOD — Expanded отдаёт списку оставшееся место
Column
Text "Заголовок"
Expanded
ListView
Text "Строка 1"
Text "Строка 2"
Скрытие строк вместо фильтрации данных
// BAD — строка прячется, но остаётся в itemCount: счётчики и «пусто» врут
ListView.builder itemCount:20
@Macros id:itemBuilder index:0
Visibility visible:{index % 2 == 0}
Text $"Строка ${index}"
Отфильтруйте данные генератором (
FilterData
), а
itemCount
возьмите из его
count
.
Best practices
-
Задавайте
itemExtent, если строки одной высоты: список не тратит время на измерение. -
Вложенный список
(внутри другого скроллера) —
shrinkWrap:trueиphysics:never. -
Не копируйте макрос в цикле вручную
: то, что повторяется сотни раз, выводится
ListView.builder. - Если нужна навигация по фокусу с пульта — смотрите ReeelScroll.
See also
- SingleChildScrollView — прокрутка одного ребёнка
- GridView — та же идея, но сеткой
-
Expanded
— как дать списку место внутри
Column - ReeelScroll — список с фокусом и навигацией