1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/CarGuo-GSYFlutterBook

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
Flutter-Chat2.md 13 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
Отправлено 10.03.2025 00:06 5767d61

Чат-лист — это сцена, требующая внимательного подхода к деталям. В предыдущей статье "Flutter реализация идеального двустороннего эффекта чата, знания о скролле списка" проблема прокрутки списка при обновлении данных была решена с помощью CustomScrollView и его конфигурации center. Однако в этом случае пользователи выдвинули новую проблему:

Изображение проблемы

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

Анимированное изображение проблемы

Проблема возникает из-за добавленного center, который использовался для решения проблемы прыжков. Поскольку список отсортирован в обратном порядке (reverse) и красочный SliverList имеет всего три элемента, высота недостаточна, чтобы заполнить верхнюю часть экрана.```dart CustomScrollView( controller: scroller, reverse: true, center: centerKey, slivers: [ SliverList( delegate: SliverChildBuilderDelegate( (BuildContext context, int index) { var item = newData[index]; if (item.type == "Right") { return renderRightItem(item); } else { return renderLeftItem(item); } }, childCount: newData.length, ), ), SliverPadding( padding: EdgeInsets.zero, key: centerKey, ), SliverList( delegate: SliverChildBuilderDelegate( (BuildContext context, int index) { var item = loadMoreData[index]; if (item.type == "Right") { return renderRightItem(item); } else { return renderLeftItem(item); } }, childCount: loadMoreData.length, ), ), ], )


- `center` является начальной точкой привязки списка; мы установили эту точку как `SliverPadding`, и поскольку список отсортирован в обратном порядке, начальная точка находится в нижней части экрана;
- Красочный старый `SliverList` находится ниже точки `center`; так как список отсортирован в обратном порядке, он фактически представляет собой желтую область;
- Несмотря на то что зелёный `SliverList` получил новые данные, высота области выше точки `center` всё ещё недостаточна, поэтому образуется пустое пространство в верхней части желтого `SliverList`.

![Изображение](http://img.cdn.guoshuyu.cn/20220328_Flutter-Chat2/image3)

Рассматривая эту проблему, можно выделить ключевой момент в использовании свойства `reverse`. В сравнении с требованиями чата WeChat и QQ, когда нет данных, сообщения должны отображаться с вершины экрана. Поэтому нам нужно будет скорректировать реализацию списка, следуя примеру WeChat/QQ.

![Изображение](http://img.cdn.guoshuyu.cn/20220328_Flutter-Chat2/image4)

Как показывает ниже приведённый код, были сделаны оптимизации для нового сценария взаимодействия:

- Удалено использование свойства `reverse` у компонента `CustomScrollView`;
- Обмен местами двух `SliverList`, переместив список, отвечающий за загрузку старых данных (`old`), перед `center`.```dart
CustomScrollView(
  controller: scroller,
  center: centerKey,
  slivers: [
    SliverList(
      delegate: SliverChildBuilderDelegate(
        (BuildContext context, int index) {
          var item = loadMoreData[index];
          if (item.type == "Справа")
            return renderRightItem(item);
          else
            return renderLeftItem(item);
        },
        childCount: loadMoreData.length,
      ),
    ),
    SliverPadding(
      padding: EdgeInsets.zero,
      key: centerKey,
    ),
    SliverList(
      delegate: SliverChildBuilderDelegate(
        (BuildContext context, int index) {
          var item = newData[index];
          if (item.type == "Справа")
            return renderRightItem(item);
          else
            return renderLeftItem(item);
        },
        childCount: newData.length,
      ),
    ),
  ],
)
```Не кажется ли вам это простым решением? После запуска приложения, как показано на нижеприведенной картинке, больше не остается свободного пространства, а также отсутствует эффект "скачков" новых данных. Двунаправленное прокрутка работает корректно. А знаете ли вы причину этого?

![](http://img.cdn.guoshuyu.cn/20220328_Flutter-Chat2/image5)

На нижеприведенной картинке видна структура после изменения:

- Начальная точка данных находится в вершине страницы, поэтому проблема с пустым пространством вверху отсутствует;
- `SliverList` под `center` отображается в правильном порядке, используется для отображения новых данных;
- `SliverList` над `center` отображается в обратном порядке относительно `center`, используется для загрузки старых данных;

![](http://img.cdn.guoshuyu.cn/20220328_Flutter-Chat2/image6)

Конечно, стоит отметить одну важную деталь: **начальный набор данных первой страницы должен быть загружен через зелёный `SliverList` в правильном направлении**, так как начальная точка находится в вершине страницы. Если бы мы использовали другой `SliverList`, то первая порция данных просто не была бы видна.

Конечно, вот перевод:

В этом случае кто-то может сказать, что если сценарий выглядит как на следующем рисунке, то будет загружаться только старая информация, а новая — нет. В таком случае нижняя часть списка будет пустой.![image](http://img.cdn.guoshuyu.cn/20220328_Flutter-Chat2/image7)

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

- Если старых данных недостаточно, например, всего три записи, то ситуация с загрузкой большего количества старых данных не возникнет, поэтому прокрутка не произойдет;
- Если старых данных достаточно, они автоматически заполняют весь список;

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

Но кто-то может спросить: "И всё? Есть ли еще какие-либо оптимизации?" Например, можно добавить проверку того, находится ли список в нижней части, чтобы решить, следует ли прокручивать до последнего сообщения после получения новых данных.

Реализация этой оптимизации довольно проста. Во-первых, мы можем использовать вложенность `NotificationListener`, где основной интерес представляет параметр `notification.metrics.extentAfter`.

```dart
NotificationListener(
  onNotification: (notification) {
    if (notification is ScrollNotification) {
      if (notification.metrics is PageMetrics) {
        return false;
      }
      if (notification.metrics is FixedScrollMetrics) {
        if (notification.metrics.axisDirection == AxisDirection.left ||
            notification.metrics.axisDirection == AxisDirection.right) {
          return false;
        }
      }
    }
  },
)
``````markdown
## Получение этого значения
```dart
extentAfter = notification.metrics.extentAfter;


Эти условия используются для исключения влияния других компонентов, таких как `PageView` или `TextField`.


Что такое параметр `extentAfter`? На самом деле в `FixedScrollMetrics` есть три параметра: `extentBefore`, `extentInside` и `extentAfter`. Их взаимосвязь показана на следующем рисунке:

![Рисунок](http://img.cdn.guoshuyu.cn/20220328_Flutter-Chat2/image8)


Обычно:

- `extentInside` представляет размер области просмотра;
- `extentBefore` представляет расстояние, которое можно прокрутить вверх;
- `extentAfter` представляет расстояние, которое можно прокрутить вниз;

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

![](http://img.cdn.guoshuyu.cn/20220328_Flutter-Chat2/image9)
``````dart
if (extentAfter == 0) {
  ScaffoldMessenger.of(context).showSnackBar(SnackBar(
    content: Text("Вы уже в самом низу списка, автоматическое перемещение к новому сообщению"),
    duration: Duration(milliseconds: 1000),
  ));
  Future.delayed(Duration(milliseconds: 200), () {
    scroller.jumpTo(scroller.position.maxScrollExtent);
  });
} else {
  ScaffoldMessenger.of(context).showSnackBar(SnackBar(
    content: InkWell(
      onTap: () {
        scroller.jumpTo(scroller.position.maxScrollExtent);
      },
      child: Text("Нажмите здесь для автоматического перемещения к новому сообщению")
    ),
    duration: Duration(milliseconds: 1000),
  ));
}
```Поэтому, если рассматривать сценарий чата, создание списка чатов не является сложной задачей, но может потребовать много мелких доработок. Если у вас есть какие-либо вопросы по этому поводу, пожалуйста, оставьте свои комментарии.

> Пример кода доступен по адресу: https://github.com/CarGuo/gsy_flutter_demo/blob/master/lib/widget/chat_list_scroll_demo_page_2.dart

Опубликовать ( 0 )

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

1
https://api.gitlife.ru/oschina-mirror/CarGuo-GSYFlutterBook.git
git@api.gitlife.ru:oschina-mirror/CarGuo-GSYFlutterBook.git
oschina-mirror
CarGuo-GSYFlutterBook
CarGuo-GSYFlutterBook
master