Реализация бесконечной прокрутки (Infinite Scroll) в мобильном приложении

TRUETECH занимается разработкой, поддержкой и обслуживанием мобильных приложений iOS, Android, PWA. Имеем большой опыт и экспертизу для публикации мобильных приложений в популярные маркеты Google Play, App Store, Amazon, AppGallery и другие.
Разработка и поддержка любых видов мобильных приложений:
Информационные и развлекательные мобильные приложения
Новостные приложения, игры, справочники, онлайн-каталоги, погодные, фитнес и здоровье, туристические, образовательные, социальные сети и мессенджеры, квиз, блоги и подкасты, форумы, агрегаторы
Мобильные приложения электронной коммерции
Интернет-магазины, B2B-приложения, маркетплейсы, онлайн-обменники, кэшбэк-сервисы, биржи, дропшиппинг-платформы, программы лояльности, доставка еды и товаров, платежные системы
Мобильные приложения для управления бизнес-процессами
CRM-системы, ERP-системы, управление проектами, инструменты для команды продаж, учет финансов, управление производством, логистика и доставка, управление персоналом, системы мониторинга данных
Мобильные приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, платформы предоставления электронных услуг, платформы кешбека, видеохостинги, тематические порталы, платформы онлайн-бронирования и записи, платформы онлайн-торговли

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

Предлагаемые услуги
Показано 1 из 1 услугВсе 1735 услуг
Реализация бесконечной прокрутки (Infinite Scroll) в мобильном приложении
Средняя
от 1 рабочего дня до 3 рабочих дней
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_mobile-applications_feedme_467_0.webp
    Разработка мобильного приложения для компании FEEDME
    756
  • image_mobile-applications_xoomer_471_0.webp
    Разработка мобильного приложения для компании XOOMER
    624
  • image_mobile-applications_rhl_428_0.webp
    Разработка мобильного приложения для компании RHL
    1052
  • image_mobile-applications_zippy_411_0.webp
    Разработка мобильного приложения для компании ZIPPY
    947
  • image_mobile-applications_affhome_429_0.webp
    Разработка мобильного приложения для компании Affhome
    862
  • image_mobile-applications_flavors_409_0.webp
    Разработка мобильного приложения для компании FLAVORS
    445

Реализация бесконечной прокрутки (Infinite Scroll) в мобильном приложении

Infinite scroll реализован в каждом втором приложении и сломан примерно в половине из них. Дублирующиеся запросы при достижении конца списка, спиннер, который показывается бесконечно после ошибки сети, или список, который скачет назад при добавлении новых элементов — всё это следствия одних и тех же технических ошибок.

Главная проблема: множественные вызовы onEndReached

FlatList.onEndReached в React Native срабатывает не один раз — он может вызваться несколько раз подряд при быстром скролле, при первом рендере если контент меньше экрана, при изменении высоты компонента. Защита:

const isLoadingMore = useRef(false);

const handleEndReached = useCallback(() => {
  if (isLoadingMore.current || !hasNextPage) return;
  isLoadingMore.current = true;
  fetchNextPage().finally(() => {
    isLoadingMore.current = false;
  });
}, [hasNextPage, fetchNextPage]);

useRef вместо useState — потому что useState не успевает обновиться между двумя синхронными вызовами onEndReached.

onEndReachedThreshold={0.3} — начинаем загрузку, когда до конца осталось 30% от высоты списка. При 0.1 пользователь видит спиннер прежде чем данные загрузятся. При 0.5 — данные подгружаются слишком рано и расходуется лишний трафик.

Cursor-based vs offset пагинация

Offset-based (?page=2&limit=20) ломается при параллельном добавлении новых элементов: страница 2 сдвигается и пользователь либо пропускает элементы, либо видит дубликаты. Cursor-based (?after=cursor_value) стабилен — каждый запрос начинается строго с последнего полученного элемента.

Если API отдаёт только offset — реализуем client-side дедупликацию по id:

const uniqueItems = [...existingItems, ...newItems].filter(
  (item, index, arr) => arr.findIndex(i => i.id === item.id) === index
);

На FlutterScrollController с addListener:

_scrollController.addListener(() {
  if (_scrollController.position.pixels >=
      _scrollController.position.maxScrollExtent - 200) {
    _loadMore();
  }
});

Альтернатива — package:infinite_scroll_pagination (pub.dev). Управляет состоянием пагинации, ошибками и retry из коробки. PagingController + PagedListView — минимум бойлерплейта.

На Android с ComposeLazyListState.firstVisibleItemScrollOffset + derivedStateOf:

val shouldLoadMore by remember {
  derivedStateOf {
    val lastVisibleIndex = listState.layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0
    lastVisibleIndex >= items.size - 5 && !isLoading
  }
}

derivedStateOf гарантирует, что recomposition происходит только когда shouldLoadMore реально меняется, а не при каждом пикселе скролла.

Обработка состояний

Infinite scroll требует корректной обработки четырёх состояний:

Состояние UI
Первичная загрузка Skeleton list (не spinner по центру экрана)
Загрузка следующей страницы Footer с CircularProgressIndicator
Ошибка загрузки следующей страницы Footer с текстом ошибки + кнопка «Повторить»
Все данные загружены Footer «Больше нет элементов» или ничего

Footer-компонент добавляется как ListFooterComponent в FlatList или через itemCount: items.length + (state != DONE ? 1 : 0) в Compose.

Из практики: социальное приложение, Flutter. Лента из 1000+ постов. Жалобы на дублирующиеся посты. Оказалось: при быстром скролле _loadMore() вызывался 3–4 раза параллельно до получения первого ответа. Cursor не обновлялся — каждый запрос уходил с одним курсором. Добавили bool _isLoading флаг + ранний return — дубликаты исчезли.

Скролл к началу

При появлении новых элементов в реалтайм-ленте не прыгаем к новым постам принудительно. Показываем плавающую кнопку «N новых записей» — пользователь сам решает, когда прокрутить наверх. scrollToIndex(0) через listRef в RN или animateScrollTo(0) в Flutter.

Что входит в работу

  • Infinite scroll с cursor-based или offset пагинацией
  • Защита от множественных запросов
  • Footer: спиннер / ошибка с retry / конец списка
  • Skeleton-загрузка для первой страницы
  • Client-side дедупликация при необходимости
  • Кнопка «Новые элементы» для реалтайм-лент

Сроки

1–3 рабочих дня в зависимости от сложности типов элементов и требований к обработке ошибок. Стоимость рассчитывается индивидуально.