Чат-лист — это сцена, требующая внимательного подхода к деталям. В предыдущей статье "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`.

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

Как показывает ниже приведённый код, были сделаны оптимизации для нового сценария взаимодействия:
- Удалено использование свойства `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,
),
),
],
)
```Не кажется ли вам это простым решением? После запуска приложения, как показано на нижеприведенной картинке, больше не остается свободного пространства, а также отсутствует эффект "скачков" новых данных. Двунаправленное прокрутка работает корректно. А знаете ли вы причину этого?

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

Конечно, стоит отметить одну важную деталь: **начальный набор данных первой страницы должен быть загружен через зелёный `SliverList` в правильном направлении**, так как начальная точка находится в вершине страницы. Если бы мы использовали другой `SliverList`, то первая порция данных просто не была бы видна.
Конечно, вот перевод:
В этом случае кто-то может сказать, что если сценарий выглядит как на следующем рисунке, то будет загружаться только старая информация, а новая — нет. В таком случае нижняя часть списка будет пустой.
Да, **мы фактически переносим проблему отсутствия данных с верхней части списка в нижнюю, но это неактуально для реальных бизнес-сценариев**, поскольку при входе в список сообщений первым делом загружается полная страница данных. Поэтому:
- Если старых данных недостаточно, например, всего три записи, то ситуация с загрузкой большего количества старых данных не возникнет, поэтому прокрутка не произойдет;
- Если старых данных достаточно, они автоматически заполняют весь список;
При увеличении количества новых данных страница также будет заполнена, позволяя нормальной прокрутке и заполнению. Таким образом, этот подход кажется более логичным.
Но кто-то может спросить: "И всё? Есть ли еще какие-либо оптимизации?" Например, можно добавить проверку того, находится ли список в нижней части, чтобы решить, следует ли прокручивать до последнего сообщения после получения новых данных.
Реализация этой оптимизации довольно проста. Во-первых, мы можем использовать вложенность `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`. Их взаимосвязь показана на следующем рисунке:

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

``````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 )