Розробка корзини покупок в мобільному додатку
Корзина — це не просто список з кнопками «плюс» та «мінус». Це синхронізоване стан між локальним сховищем та сервером, яке повинно пережити переключення між додатками, втрату інтернету, logout та повторний login. Корзина, яка втрачає товари при закриванні додатку або показує застарілу ціну при оформленні замовлення — джерело прямих потерь конверсії.
Де всё йде не так
Синхронізація локального та серверного стану. Найчастіша помилка — зберігати корзину тільки на клієнті. Користувач додав товар з одного пристрою, відкрив додаток на іншому — корзина порожня. Правильна схема: оптимістичне оновлення UI + асинхронна синхронізація з сервером. У React Native з Redux Toolkit — createAsyncThunk для кожної операції (add, remove, updateQuantity), extraReducers з pending/fulfilled/rejected. При помилці — rollback до попереднього стану через immer.
Застарілі ціни та наявність. Користувач додав товар три дні тому, ціна змінилась. При відкриванні корзини робимо запрос на валідацію з поточними даними — сервер повертає актуальні ціни та статус наявності. Товари, яких немає в наявності, виділяємо візуально та блокуємо чекаут.
Свайп для видалення. SwipeRow у React Native або ReorderableList часто конфліктують з вертикальним скролом. На Android з Compose — SwipeToDismiss із Material 3 працює стабільніше. На iOS — UISwipeActionsConfiguration у нативі, Flutter — Dismissible віджет. Везді потрібна haptic feedback при досягненні порогу свайпу — UIImpactFeedbackGenerator.impactOccurred() / HapticFeedback.mediumImpact().
Архітектура корзини
CartItem {
productId: string
variantId: string // розмір, колір
quantity: number
priceSnapshot: number // ціна на момент додавання
currentPrice: number // актуальна ціна з сервера
}
variantId — обов'язкове поле, яке часто забувають. Один та той же товар у двох розмірах — два різні CartItem. Без цього перерахунок суми ломиться при змішаних варіантах.
Персистентність: AsyncStorage + сериалізація JSON у RN, Room database на Android, CoreData або UserDefaults для невеликих корзин на iOS. При наступному запуску відновлюємо локальний стан негайно, потім робимо фонову синхронізацію з сервером.
Кейс з практики: e-commerce додаток, Flutter + BLoC. Корзина "забувала" товари при force-close. Причина — HydratedBloc не був ініціалізований до першого звернення до storage. Перенесли ініціалізацію HydratedStorage в main() до runApp() — проблема ушла.
Итогова сума та скидки
Перерахунок суми — тільки на сервері при чекауті. На клієнті показуємо попередню суму (локальний розрахунок), при переході до оплати робимо фінальний розрахунок з урахуванням промокодів, скидок лояльності, доставки. Це попереджає розходження сум.
Промокод поле — окремий UX-елемент. При введенні невалідного коду — shake animation на полі + текст помилки під ним, не toast.
Що входить у роботу
- Список товарів у корзині з зображенням, назвою, варіантом, ціною
- Зміна кількості (інпут або +/– кнопки) з валідацією min/max
- Свайп для видалення з undo через snackbar (5 секунд)
- Итогова сума з розбивкою (товари, доставка, скидка)
- Поле для промокоду з валідацією
- Порожнє стан корзини з CTA
- Синхронізація з сервером + обробка offline
- Перейти до оформлення замовлення з валідацією наявності товарів
Строки
2–3 робочих дні. Якщо потрібна синхронізація між пристроями, інтеграція з програмою лояльності або складна логіка скидок — 3–5 днів. Вартість розраховується індивідуально.







