Как исправить «переход к началу каждого элемента» при прокрутке вверх во Flutter ListView элементов FutureBuilder?

Я только начал изучать Flutter с помощью курса, и один из проектов — написать приложение Hacker News reader. Необходимы два HTTP-запроса: один для получения верхнего списка идентификаторов и один для каждой статьи с использованием ее идентификатора.

В отличие от версии The Boring Show от Google, мне нужно иметь возможность запрашивать каждую статью по мере того, как пользователь прокручивает вниз до общего количества популярных статей (~ 500). Я также хотел попробовать более простой способ, чем многие потоки, преобразователи и обходные пути, которые использовались в моем курсе.

Я использую StreamBuilder, чтобы получить лучшие идентификаторы из потока через bloc и FutureBuilder для каждой строки ListView.builder, где статья извлекается из этого bloc. Данные извлекаются либо из HTTP-запроса, либо из кеша (базы данных sqflite).

Он отлично работает при прокрутке вниз, но при прокрутке вверх он переходит к началу каждого элемента, кроме первых 4 элементов (потому что они уже построены в этот момент).

Я пробовал разные структуры и шаблоны в течение нескольких дней, и они всегда ведут себя одинаково, когда я использую FutureBuilder внутри ListView.builder. Пробовал в эмуляторе и на своем Pixel 3 XL в режиме отладки и выпуска.

Метод Boring Show отправки ограниченного количества статей из StreamBuilder в ListView.builder работает, потому что FutureBuilder не нужен, но нет возможности получить больше статей.

Кстати, из того, что я могу сказать, элементы НЕ извлекаются повторно при прокрутке вверх, даже если ConnectionState установлено на waiting для каждой сборки.

Я добавил видео проблемы на YouTube. Надеюсь, что это работает:

Видео YouTube о проблеме ListView при прокрутке вверх

Вот класс, в котором отображается список. Я объединил виджеты, чтобы сделать его компактным:

class TopList extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final StoriesBloc bloc = StoriesProvider.of(context);
    return StreamBuilder<List<int>>(
        stream: bloc.topList,
        builder: (context, snapshot) {
          return snapshot.hasData
              ? ListView.builder(
                  itemBuilder: (context, int index) {
                    return FutureBuilder<ItemModel>(
                        key: Key(snapshot.data[index].toString()),
                        future: bloc.getNewsItem(snapshot.data[index]),
                        builder: (context, snapshot) {
                          switch (snapshot.connectionState) {
                            case ConnectionState.none:
                            case ConnectionState.active:
                            case ConnectionState.waiting:
                              print('${snapshot.connectionState} for $index');
                              return Text('Loading...');
                            case ConnectionState.done:
                              return buildItem(snapshot.data, index);
                          }
                        });
                  },
                )
              : Center(
                  child: CircularProgressIndicator(),
                );
        });
  }

  Widget buildItem(ItemModel item, int index) {
    print('Building row $index');
    return ListTile(
      key: Key(index.toString()),
      leading: CircleAvatar(
        child: Text('${index + 1}', style: TextStyle(fontSize: 14)),
        radius: 16,
      ),
      title: Text(item.title),
      subtitle: Text('${item.score}'),
      trailing: Column(
        children: <Widget>[
          Icon(Icons.comment),
          Text('${item.descendants}'),
        ],
      ),
    );
  }
}

от flutter doctor:

Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel dev, v1.5.8, on Microsoft Windows [Version 10.0.17763.437], locale en-US)

[√] Android toolchain - develop for Android devices (Android SDK version 28.0.3)
[√] Android Studio (version 3.4)
[√] Android Studio (version 3.5)
[√] VS Code (version 1.32.3)
[√] Connected device (1 available)

• No issues found!

Я ожидал, что прокрутка вверх и вниз будет плавной, а текст «Загрузка...» будет отображаться для загружаемых элементов.

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

I/flutter (13405): ConnectionState.waiting for 17
I/flutter (13405): Building row 17
I/flutter (13405): ConnectionState.waiting for 18
I/flutter (13405): Building row 18
I/flutter (13405): ConnectionState.waiting for 19
I/flutter (13405): Building row 19
I/flutter (13405): ConnectionState.waiting for 20
I/flutter (13405): Building row 20
I/flutter (13405): ConnectionState.waiting for 21
I/flutter (13405): Building row 21
I/flutter (13405): ConnectionState.waiting for 22
I/flutter (13405): Building row 22
I/flutter (13405): ConnectionState.waiting for 23
I/flutter (13405): Building row 23
I/flutter (13405): ConnectionState.waiting for 6
I/flutter (13405): Building row 6
I/flutter (13405): ConnectionState.waiting for 5
I/flutter (13405): Building row 5
I/flutter (13405): ConnectionState.waiting for 4
I/flutter (13405): Building row 4
I/flutter (13405): ConnectionState.waiting for 3
I/flutter (13405): Building row 3
I/flutter (13405): ConnectionState.waiting for 2
I/flutter (13405): Building row 2
I/flutter (13405): ConnectionState.waiting for 1
I/flutter (13405): Building row 1
I/flutter (13405): ConnectionState.waiting for 0
I/flutter (13405): Building row 0

Проблема может быть очевидной, но я не могу понять ее.


person RichR    schedule 03.05.2019    source источник
comment
Мне кажется, я слышу эхо своего поста здесь :) Это из-за недели ввода/вывода?   -  person RichR    schedule 11.05.2019


Ответы (1)


Починил это!

Хорошо, получил предложение от «купить дип кек» в группе флаттера Discord разместить точно такой же виджет, ListTile, с фиктивными данными, пока загружается будущее из каждого FutureBuilder. Это решило проблему.

person RichR    schedule 14.05.2019
comment
На самом деле, для этого должно быть лучшее решение... Я пока не нашел. - person Gyuri Majercsik; 08.08.2019