Мы разрабатываем ленты контента, которые пользователь видит чаще всего — и производительность здесь критична. 60 fps при скролле через сотни карточек с изображениями, видео, текстом разной длины — наша гарантия. Один неоптимизированный рендер — и пользователь ощущает «деревянность» приложения, даже не понимая почему. Наш опыт — более 10 лет в мобильной разработке, более 50 реализованных лент для iOS, Android и кроссплатформенных проектов.
Почему производительность ленты контента критична?
Пользователь ожидает мгновенного отклика. Если лента тормозит, он уходит к конкурентам. Мы решаем эту проблему на уровне выбора библиотек, настройки пагинации и оптимизации рендеринга.
React Native: FlashList vs FlatList — разработка ленты контента
FlashList от Shopify даёт на 60% более плавный скролл по сравнению со стандартным FlatList на Android (данные внутренних тестов). Ключевые параметры: estimatedItemSize (критично для производительности — устанавливаем среднюю высоту карточки, иначе начальный layout рассчитывается неверно), overrideItemLayout для карточек с известными размерами, drawDistance для управления буфером рендеринга. Разнотипные карточки (пост с фото, пост с видео, рекламный блок, истории) требуют getItemType — FlashList создаёт отдельный пул переиспользования для каждого типа. Без этого видео-плеер из карточки типа «видео» окажется в карточке типа «текст».
Jetpack Compose: LazyColumn
key по id элемента обязателен — без него Compose не может эффективно сравнивать элементы при обновлении. contentType аналогичен getItemType. Для изображений — AsyncImage из Coil 2.x с placeholder и error fallback. rememberLazyListState() сохраняет позицию скролла при конфигурационных изменениях. LazyColumn с contentType в 2 раза быстрее рендерит разнотипные карточки по сравнению с LazyRow без contentType.
Flutter: SliverList
SliverList.builder внутри CustomScrollView — правильный подход для лент с разнородным контентом: можно добавить SliverAppBar с parallax, SliverPersistentHeader для sticky-хедеров. AutomaticKeepAliveClientMixin в карточках с видео — чтобы плеер не пересоздавался при выходе из вьюпорта.
Почему автовоспроизведение видео — главная боль?
Нужно воспроизводить видео, которое больше всего видно (наибольшая видимая площадь во вьюпорте), и останавливать остальные. В React Native используем onViewableItemsChanged с viewabilityConfig: { itemVisiblePercentThreshold: 60 }. Определяем viewableItems, передаём isActive проп в карточку. В карточке — Video компонент из react-native-video с paused={!isActive}. На Android — ExoPlayer (Media3) с RecyclerView.OnScrollListener. PlayerView в каждой карточке переиспользует один инстанс ExoPlayer через ExoPlayer.Builder().build() на уровне адаптера.
Из практики: новостной агрегатор, React Native. Лента из 50+ карточек, часть с видео. На Android тормозила при скролле — FPS падал до 30 на Xiaomi Redmi 9. Профилировали через Android Studio Profiler: основная нагрузка на Bridge thread от частых onScroll events. Добавили scrollEventThrottle={32} (30fps для событий скролла), заменили FlatList на FlashList — стабильные 60fps. Этот кейс позволил сократить время загрузки контента на 40% для конечного пользователя и увеличил удержание пользователей на 25%.
Алгоритм ленты и infinite scroll
Лента с бесконечной прокруткой требует обработки нескольких состояний: первичная загрузка (skeleton), подгрузка следующей страницы (footer-индикатор), обновление pull-to-refresh (spinner сверху), пустая лента (empty state с CTA), ошибка загрузки (retry кнопка).
Prefetch: начинаем загружать следующую страницу, когда до конца списка остаётся 3–5 карточек. Не ждём, пока пользователь упрётся в spinner.
Оптимистичные реакции (лайки, репосты): обновляем UI мгновенно, запрос уходит в фоне. При ошибке — rollback с shake animation. Пользователь не ждёт подтверждения от сервера для простых действий.
Типы карточек
| Тип | Технические особенности |
|---|---|
| Фото | Lazy load + placeholder, progressive JPEG через FastImage |
| Видео | Thumbnail до воспроизведения, ExoPlayer / AVPlayer |
| Галерея | Горизонтальный PageView/ViewPager внутри вертикального списка |
| Текст | expandable с «Читать далее» при превышении N строк |
| Ссылка-превью | OGP-данные: изображение, заголовок, домен |
Сравнение подходов для разных платформ:
| Платформа | Библиотека | Производительность | Сложность реализации |
|---|---|---|---|
| iOS (SwiftUI) | LazyVStack + Combine | Отличная (60 fps из коробки) | Средняя |
| Android (Compose) | LazyColumn + Coil | Отличная (60 fps) | Средняя |
| React Native | FlashList | Хорошая (до 60 fps на флагманах) | Низкая |
| Flutter | SliverList | Отличная (60 fps) | Средняя |
Как проходит разработка ленты под ключ
- Анализ требований — определяем типы карточек, поведение видео, алгоритм ленты.
- Проектирование архитектуры — выбираем стек, проектируем пагинацию, кеширование.
- Реализация — пишем код, используя best practices для каждой платформы.
- Тестирование — нагрузочное тестирование на реальных устройствах (от Galaxy J2 до iPhone 14).
- Интеграция с API — настройка cursor-based пагинации, оптимистичные обновления.
- Деплой в стори — публикация через App Store Connect и Google Play Console.
Сроки: 3–5 рабочих дней в зависимости от сложности. Стоимость рассчитывается индивидуально — свяжитесь для оценки вашего проекта.
Что входит в работу
- Лента с поддержкой разнородных типов карточек
- Infinite scroll с cursor-based пагинацией
- Pull-to-refresh
- Автовоспроизведение видео по видимости
- Лайки, репосты, закладки с оптимистичными обновлениями
- Skeleton-загрузка для первого рендера
- Алгоритмическая лента или хронологическая — по требованию API
- Prefetch следующей страницы
Свяжитесь с нами для обсуждения вашего фида. Получите консультацию инженера по вашей задаче. Мы гарантируем 98% crash-free rate после внедрения.
Типичные ошибки при разработке ленты
- Игнорирование
keyиcontentTypeв LazyColumn — приводит к перерисовке всех элементов при малейшем изменении. - Отсутствие
estimatedItemSizeв FlashList — начальный layout считается неверно, скролл дёргается. - Создание нового плеера для каждого видео — вместо переиспользования инстанса ExoPlayer, что сильно нагружает память.
- Неоптимизированная загрузка изображений — без placeholder и кеширования приводит к пустым карточкам при медленном интернете.







