Lesson 3

Урок 3. ListView бесконечный прокручиваемый список

Егор Долгов · 05.07.2019
This article was written for an older Flutter version and is being updated. Some code may not compile on the current version.

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

Под бесконечным списком мы будем понимать список элементов, который пополняется по мере необходимости, в нашем уроке – когда существующие элементы уже отображены. В мобильных приложениях такие списки повсюду: новостные ленты, списки сообщений, фотогалереи и т.п.

Класс ListView имеет несколько конструкторов, в этом уроке мы рассмотрим два из трех.

1. Простой список с помощью конструктора ListView() – по умолчанию

Создадим простой список текстовых элементов с использованием класса ListView .

Конструктор по умолчанию ListView() строит список из заданных элементов в параметре children .

Конструктор по умолчанию ListView() строит список из заданных элементов в параметре children
Конструктор по умолчанию ListView() строит список из заданных элементов в параметре children
import 'package:flutter/material.dart';

void main() =>  runApp(
    new MaterialApp(
        debugShowCheckedModeBanner: false,
        home: new Scaffold(
          body: new ListView(children: [
            new Text('0000'),
            new Divider(),
            new Text('0001'),
            new Divider(),
            new Text('0010'),
          ])// ListView
        )// Scaffold
    )// MaterialApp
);
  • new Text('') – создание текстового виджета
  • new Divider() – виджет-разделитель, отображается как горизонтальная или вертикальная полоса (в зависимости от направления расположения виджетов)

2. Бескочнечный список с помощью ListView.builder() – список по требованию

Конструктор ListView.builder() выводит элементы списка по требованию – вызывает функцию в параметре itemBuilder для создания новых элементов списка при прокрутке, позволяет создавать бесконечные списки виджетов.

Конструктор ListView.builder() выводит элементы списка по требованию
Конструктор ListView.builder() выводит элементы списка по требованию
import 'package:flutter/material.dart';

void main() =>  runApp(
    new MaterialApp(
        debugShowCheckedModeBanner: false,
        home: new Scaffold(
          body: new ListView.builder(
            itemBuilder: (context, index) {
              return new Text('строка $index');
            },
          )// ListView.builder
        )// Scaffold
    )// MaterialApp
);
  • itemBuilder – обязательный параметр для конструктора ListView.builder() , в качестве значения он принимает функцию, которая возвращает виджет-элемент.
  • index – переменная типа int , принимает значение от 0 до N, где N – натуральное число.
  • return new Text('строка $index'); – возвращает текстовый виджет со значением index .

Функция в itemBuilder вызывается каждый раз с новым значением index , когда нужно заполнить экран или область которую занимает ListView .

3. Реальное использование ListView.builder() для бесконечных списков

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

Нам понадобится создать класс MyBody от супер-класса StatefulWidget (который мы рассмотрели в прошлом уроке ). MyBody будет вызывать функцию состояния через createState() => new MyBodyState();

Поэтому нам понадобится еще объявить класс MyBodyState от супер-класса State .

В классе MyBodyState мы объявим List<String> array = []; скрытое поле-массив, в котором будут текстовые значения для элемента списка класса Text() . А функция build() будет возвращать список ListView.builder .

import 'package:flutter/material.dart';

class MyBody extends StatefulWidget {
  @override
  createState() => new MyBodyState();
}

class MyBodyState extends State<MyBody> {
  List<String> _array = [];

  @override
  Widget build(BuildContext context) {
    return new ListView.builder(itemBuilder: (context, i){
      print('num $i : нечетное = ${i.isOdd}');

      if(i.isOdd) return new Divider();

      final int index = i ~/ 2;

      print('index $index');
      print('length ${_array.length}');

      if (index >= _array.length) _array.addAll(['$index', '${index + 1}', '${index + 2}']);

      return new ListTile(title: new Text(_array[index]));
    });
  }
}

void main() =>  runApp(
    new MaterialApp(
        debugShowCheckedModeBanner: false,
        home: new Scaffold(body: new Center(child: new MyBody(),))
    )
);

Рассмотрим подробно функцию build() в нашем классе MyBodyState:

  • return new ListView.builder(itemBuilder: (context, i){ – Сама функция build в нашем примере возвращает список ListView с конструктором по требованию .builder
  • print('num $i : нечетное = ${i.isOdd}'); – выводим в консоль строку значение переменной i , пример вывода: num 0 : нечетное = false num 1 : нечетное = true num 2 : нечетное = false и т.д.
  • if(i.isOdd) return new Divider(); – если значение переменной i нечетное, то возвращается виджет-разделитель и на этом выполнение функции для данного i завершается.
  • final int index = i ~/ 2; – объявляем переменную index типа int , в качестве значения, результат целочисленного деления переменной i на 2. На нечетном значении переменной i = {1, 3, 5, 7, 9, 11, …} мы создает виджет разделитель а на четном значении i = {0, 2, 4, 6, 8, 10, 12, …} виджет строку. Для того чтобы в текстовом виджете использовать индексы по порядку а не только четные значения i , мы используем целочисленное деление от четных значений i и получаем index = {0, 1, 2, 3, 4, 5, 6, …}
  • print('index $index'); – выводим в консоль значение переменной index , пример вывода: index 0 index 1 index 2 и т.д.
  • print('length ${ array.length}'); – выводим в консоль длину массива array объявленного в классе MyBodyState , пример вывода: length 0 length 3 length 6 и т.д.
  • if (index >= array.length) array.addAll(['$index', '${index + 1}', '${index + 2}']); if (index >= array.length) – если значение index больше или равно числу элементов в массиве array_array.addAll([]) – добавить в массив array все элементы из списка []'$index' – строковое значение переменной index'${index + 1}' – строковое значение переменной index увеличенное на единицу

В данном примере мы в массив array добавляем сразу три новых элемента, по аналогии как это может происходить при реальном получение данных с сервера. Так, например, получая список новостей с сервера мы загружаем их по 20 штук сразу (пример), а потом при необходимости загружаем следующие 20. Загружать все новости, особенно если их очень много, нет никакого смысла, т.к. предполагаем, что пользователь прочтет от 1 до 20 новости наверняка, и, если нужно, мы загрузим остальные.

В нашем примере мы могли написать так: array.add('$index'); – добавить один элемент в массив.

  • return new ListTile(title: new Text( array[index])); – результат функции, мы возвращаем виджет элемент-списка с виджетом текста return new Text( array[index]); – можно было сделать так, но с классом ListTile элементы нашего списка выглядят привлекательнее array[index] – возвращает значение из массива

Рекомендуем изучить сообщения, которые выводятся в консоль:

Бесконечный список ListView.builder с разделителями
Бесконечный список ListView.builder с разделителями
Сообщения консоли при добавлении нового элемента в список ListView
Сообщения консоли при добавлении нового элемента в список ListView

По мере того как Вы будете прокручивать список в эмуляторе или на смартфоне, список будет создавать новые элементы списка, и в консоли будут появляться новые сообщения.

4. Задание для практики: бесконечный список степени числа 2 (два)

  1. Измените строковый список List<String> array = []; на числовой List<int> array = [];
  2. Попробуйте изменить приведенный выше код для вывода бесконечного списка степени числа 2 с помощью математической функции pow() и без нее.
  3. Результат должен быть похожим как на изображении ниже
Бесконечный список степени числа 2 (два)
Бесконечный список степени числа 2 (два)
  1. Можно ли сделать на Flutter бесконечный список для степени числа 2 с помощью типа int или double ?
  2. Какие бесконечные списки Вы можете придумать?

Для выполнения задания вам понадобится:

  • import 'dart:math'; подключить библиотеку с математическими функциями
  • pow(3, 2) – функция возведение числа в степень (3 в степени 2)
  • pow(3, 2).toString() – вернет результат функции в строковом виде
  • index.toDouble() – преобразовать в тип double
  • pow(2.0, 3).toStringAsFixed(0 ) – преобразовать значение функции типа double с точности до целого (чтобы не выводить значение с точкой и нулем)

Ваши вопросы и ответы в виде кода программы присылайте на нашу почту [contact@flutter.ru](mailto:contact@flutter.ru?subject=Урок № 3), в теме письма обязательно указывайте «Урок № 3».