У цій статті ми розповімо про налаштування архітектури GetX у Flutter без витоків пам'яті. Зауважимо: коли стартап замовляє Flutter-застосунок, вибір управління станом стає ключовим. GetX обіцяє мінімум коду, але без правильної архітектури проєкт ризикує витоками пам'яті та проблемами з масштабуванням. Правильне налаштування архітектури GetX запобігає витокам і пришвидшує розробку. Наприклад, нещодавно ми запустили застосунок для доставки за 14 днів: профіль користувача, каталог товарів, корзина з замовленням. GetX дозволив підключити всі три шари за один день, але без правильної архітектури проєкт міг піти на дно через витоки пам'яті та проблеми з тестуванням. Економія на тестуванні при використанні Bindings досягає $2000–3000, а вартість виправлення витоків у великому проєкті може перевищувати $5000. Отримайте безкоштовний аналіз вашого коду, щоб оцінити потенційну економію. Вартість налаштування архітектури GetX — від $500. Ми надаємо гарантію на усунення витоків протягом 2 тижнів. Наші сертифіковані інженери мають понад 5 років досвіду.
Чому GetX підходить для MVP?
GetX дозволяє запустити продукт за тижні, а не місяці. GetX запускає MVP в 2 рази швидше, ніж Bloc. Get.to(SecondScreen()) замінює десятки рядків Navigator.push. controller.name.obs та Obx(() => Text(controller.name)) дають реактивність без BuildContext. Для команди з двох розробників це знижує поріг входу та скорочує кількість коду на 30–50% у порівнянні з Bloc. Повний цикл розробки MVP з GetX економить до 40% бюджету порівняно з Bloc.
class ProfileController extends GetxController {
final UserRepository _repository;
ProfileController(this._repository);
final profile = Rxn<UserProfile>();
final isLoading = false.obs;
final error = RxnString();
@override
void onInit() {
super.onInit();
loadProfile(Get.arguments as String);
}
Future<void> loadProfile(String userId) async {
isLoading.value = true;
error.value = null;
try {
profile.value = await _repository.getProfile(userId);
} catch (e) {
error.value = e.toString();
} finally {
isLoading.value = false;
}
}
}
У віджеті Obx(() => ...) підписується тільки на використані .obs-змінні:
Obx(() => controller.isLoading.value
? const CircularProgressIndicator()
: ProfileView(profile: controller.profile.value!),
)
Реєстрація залежностей через Get.lazyPut(() => ProfileController(Get.find())).
Як уникнути витоків пам'яті при використанні GetX?
Головна пастка — глобальний Get.put(), який не звільняє контролери при виході з екрану. Основна мета налаштування архітектури GetX — уникнути витоків пам'яті. Правильний спосіб — Bindings.
- Визначте
Bindings для кожного модуля.
- Вкажіть його в маршрутах
GetPage.
- Всередині
dependencies() використовуйте Get.lazyPut.
- Контролери автоматично знищуються при залишенні екрану.
class ProfileBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut(() => UserRepositoryImpl());
Get.lazyPut(() => ProfileController(Get.find()));
}
}
GetPage(
name: Routes.profile,
page: () => const ProfileScreen(),
binding: ProfileBinding(),
)
Binding створює контролер при вході на екран та знищує при виході. Це виправляє витоки, які легко отримати з Get.put() без явного управління. Офіційна документація GetX попереджає про проблеми з життєвим циклом — використовуйте Bindings завжди.
Що входить у налаштування архітектури GetX
- Аудит поточного коду на витоки та неоптимальні патерни.
- Діаграма залежностей та структура папок.
- Реалізація Bindings для всіх екранів.
- Заміна глобальних
Get.put() на локальні прив'язки.
- Юніт-тести контролерів з mockito.
- Документація з архітектури та навчання команди.
- Підтримка протягом 2 тижнів після здачі.
Порівняння: GetX vs Bloc vs Riverpod
| Критерій |
GetX |
Bloc |
Riverpod |
| Boilerplate |
Мінімальний |
Середній |
Низький |
| Reactive state |
.obs |
Stream |
AsyncValue |
| DI included |
Так |
Ні |
Частково (Provider) |
| Navigation |
Get.to() |
Через Navigator 2.0 |
Через Navigator |
| Testing complexity |
Висока (Service Locator) |
Середня |
Низька |
| Learning curve |
Низька |
Середня |
Середня |
GetX виграє за швидкістю старту, але для складних проєктів краще розглянути Riverpod — він поєднує простоту з контролем. GetX скорочує обсяг коду на 30–50% порівняно з Bloc, що прискорює MVP-розробку.
Етапи налаштування архітектури GetX під ключ
| Етап |
Тривалість |
Опис |
| Аналіз |
0.5–1 день |
Аудит існуючого коду на витоки та неоптимальні патерни |
| Проектування |
0.5 дня |
Діаграма залежностей, структура папок, Bindings |
| Реалізація |
1–2 дні |
Переписування контролерів, заміна Get.put() на Bindings |
| Тестування |
0.5–1 день |
Юніт-тести з mockito, покриття контролерів |
| Деплой та Doc |
0.5 дня |
Складання релізу, документація, навчання команди |
Результат: архітектура, готова до масштабування, без витоків пам'яті. Середня економія на виправленні помилок — $2000.
Чек-лист перед здачею архітектури
- [ ] Всі контролери використовують Bindings
- [ ] Немає глобальних
Get.put() (крім сінглтонів типу Dio)
- [ ] Кожен
.obs використовується в Obx
- [ ] Навігація обробляє App Links (Universal Links)
- [ ] Написані тести на основні сценарії
- [ ] Документація по структурі та залежностях
Типові помилки при старті з GetX
- Використання
Get.put() в корені застосунку — призводить до витоків. Вихід: завжди використовуйте Bindings.
- Забули обгорнути віджет в
Obx — реактивність не спрацьовує без помилок компіляції. Перевіряйте кожен .obs.
- Глобальні контролери, які не знищуються — пам'ять зростає до 50 МБ при скролі. Рішення:
Bindings знищують контролер при виході з екрану.
- Навігація через
Get.to() без обробки deep link — проблеми при web-збірці. Для web використовуйте Navigator 2.0.
Терміни налаштування
Налаштування архітектури GetX «з нуля» займає 1–2 дні. Рефакторинг існуючого застосунку з усуненням витоків — 3–5 днів. Точна оцінка залежить від обсягу екранів та поточного коду. Наші інженери мають понад 5 років досвіду з Flutter та реалізували 30+ проєктів. Зв'яжіться з нами для оцінки вашого проєкту — ми безкоштовно проаналізуємо код та запропонуємо план робіт. Замовте налаштування архітектури GetX для вашого MVP. Отримайте консультацію з оптимізації вашого Flutter-застосунку.
MVVM, Clean Architecture, BLoC, VIPER, TCA: проєктуємо архітектуру під ключ
Додаток зібрано в одному ViewController на 2000 рядків. Мережеві виклики, бізнес-логіка, оновлення UI — все в одному місці. Додати нову фічу без регресії складно, написати тест неможливо. Це не «поганий код» — це відсутність архітектури. І це трапляється частіше, ніж можна очікувати, навіть у production-додатках з мільйоном користувачів.
Ми проєктуємо архітектуру під ключ: від вибору паттерну до повної структури проєкту з тестами та документацією. За 7–10 днів отримуєте чистий модульний код, готовий до масштабування. Ваша команда зможе додавати нові фічі без ризику зламати існуючі, а тести покриють ключову логіку вже на старті. Архітектурні паттерни в мобайлі вирішують одне завдання: відокремити UI від логіки так, щоб кожна частина була тестованою та замінною.
MVVM — базовий паттерн
Model-View-ViewModel — стандарт для iOS (SwiftUI + Combine/async, UIKit + Combine) та Android (Jetpack ViewModel + StateFlow + Compose). ViewModel містить стан UI та бізнес-логіку. View лише відображає стан і передає наміри користувача до ViewModel. Model — дані та їх джерело.
Ключове правило: ViewModel не знає про UIKit або Android View-класи. Немає імпортів UIKit, немає Context-залежностей (крім Application context через Hilt). Це гарантує тестованість: ViewModel тестується як чистий Kotlin/Swift-код без Android Instrumented Test.
MVVM закриває 70% потреб. Інші 30% — де потрібна строга ізоляція фіч, масштабування команди, складний flow управління станом.
Clean Architecture — коли MVVM недостатньо
Додає шари поверх MVVM:
Domain-шар — бізнес-логіка, незалежна від платформи. UseCase (або Interactor) містить одне бізнес-правило: GetUserOrdersUseCase, PlaceOrderUseCase. Залежить лише від інтерфейсів (protocol/interface), не від конкретних реалізацій.
Data-шар — реалізація репозиторіїв. OrderRepositoryImpl реалізує OrderRepository з domain. Знає про Retrofit, Room, UserDefaults. ViewModel не знає, звідки дані — з мережі чи кешу.
Presentation-шар — ViewModel + View. Знає про Domain, не знає про Data.
Dependency rule: залежності спрямовані лише всередину. Domain не залежить ні від чого. Data та Presentation залежать від Domain.
Presentation → Domain ← Data
Це дає можливість підміняти реалізацію: тест використовує in-memory репозиторій замість мережевого, інтерфейс залишається тим самим.
Практичне зауваження: Clean Architecture додає файли та шари. Для невеликого додатку це overhead. Виправдано від ~15 фіч та при команді 3+ розробників.
BLoC для Flutter — передбачуваний потік станів
BLoC (Business Logic Component) — стандартний паттерн у Flutter-спільноті. Бібліотека flutter_bloc реалізує його через два типи: Bloc (Event → State) та Cubit (State без Events, лише методи).
Bloc обробляє Event та емітує новий State через on<EventType> хендлери. Стан імутабельний — новий об'єкт на кожну зміну. BlocBuilder перемальовує лише ту частину дерева, де змінився state.
// Подія
alias CartEvent {}
class AddItemToCart extends CartEvent {
final String productId;
AddItemToCart(this.productId);
}
// Стан
alias CartState {}
class CartLoaded extends CartState {
final List<CartItem> items;
CartLoaded(this.items);
}
// Bloc
class CartBloc extends Bloc<CartEvent, CartState> {
CartBloc(this._cartRepository) : super(CartLoaded([])) {
on<AddItemToCart>(_onAddItem);
}
Future<void> _onAddItem(AddItemToCart event, Emitter<CartState> emit) async {
final current = state as CartLoaded;
final updated = await _cartRepository.addItem(event.productId);
emit(CartLoaded(updated));
}
}
Перевага BLoC — тестованість. blocTest з bloc_test пакету дозволяє перевірити: при такому-то Event, з таким-то початковим State, BLoC має емітувати такий-то State. Без UI, без моків для Flutter-фреймворку.
VIPER — для великих iOS-проєктів
VIPER (View, Interactor, Presenter, Entity, Router) — найбільш строгий поділ обов'язків для iOS. Кожен компонент має протокол та конкретну реалізацію.
- View — лише UI, делегує все Presenter
- Interactor — бізнес-логіка, робота з мережею та даними
- Presenter — посередник між View та Interactor, форматує дані для View
- Entity — моделі даних (чисті структури)
- Router — навігація між модулями
Кожен модуль (екран або фіча) — окремий VIPER-модуль. Це виключає coupling між фічами та дозволяє великим командам працювати паралельно без конфліктів.
Ціна: багато файлів, багато протоколів. Шаблонний код генерується через Sourcery або кастомні Xcode-шаблони. VIPER виправданий для додатків з 10+ розробниками та 50+ екранами.
TCA (The Composable Architecture)
TCA від Point-Free — більш сучасна альтернатива VIPER для iOS/macOS. Основні концепції: State (імутабельний стан фічі), Action (всі можливі події), Reducer (State + Action → новий State + Effect), Store (зберігає State, обробляє Actions).
Scope дозволяє composable будувати великі фічі з маленьких: батьківський Reducer делегує частину State дочірньому. Кожна фіча тестується ізольовано через TestStore з точним контролем над Effects.
TCA має круту криву навчання, але дає передбачуваність, яку складно отримати іншим способом: кожна зміна стану — явний Action з конкретним джерелом.
Як обрати архітектуру мобільних додатків?
Оцінимо проєкт за 1 день — підберемо архітектуру з урахуванням розміру команди, платформи та планів зростання.
| Паттерн |
Платформа |
Команда |
Коли обирати |
| MVVM |
iOS, Android, Flutter |
1–5 |
Стартовий стандарт, MVP, невеликі проєкти |
| MVVM + Clean |
iOS, Android |
3–10 |
Середні проєкти, тестованість критична |
| BLoC |
Flutter |
2–8 |
Flutter з передбачуваним state management |
| VIPER |
iOS |
5–20 |
Великі iOS-проєкти, модульна архітектура |
| TCA |
iOS/macOS |
3–15 |
Сувора тестованість, Swift Concurrency |
Універсальної відповіді немає. Архітектуру обирають під розмір команди, вимоги до тестованості та горизонт підтримки додатку.
Що входить у нашу роботу
Процес складається з кількох кроків:
-
Аудит поточної архітектури (якщо додаток вже існує) — виявимо вузькі місця та регресійні зони.
-
Проєктування модульної структури з чіткими межами шарів та правилами залежностей.
-
Створення каркасу проєкту (Scaffold) з впровадженням DI, організації папок та налаштування лінтерів.
-
Написання юніт-тестів для шару домену та ViewModel — мінімум 80% покриття ключових use case.
-
Підготовка документації — архітектурні діаграми, README з правилами модифікації коду, інструкція для онбордингу нових розробників.
-
Передача робочого репозиторію з CI-пайплайном (GitHub Actions / Bitrise), налаштованим запуском тестів та статичним аналізом.
Все це входить у вартість проєктування. Додатково — підтримка на етапі впровадження: консультації команди, code review перших pull request.
Що відбувається без архітектури
Типовий сценарій через 18 місяців без архітектури: 40% часу розробки йде на дебаг регресій. Новий розробник розбирається в коді тиждень перед тим, як зробити перший PR. Тести не пишуться, «тому що складно мокувати». Додавання нової фічі вимагає розуміння половини кодової бази.
Вибір архітектури на старті — це інвестиція з поверненням через 3–6 місяців. За нашими даними, правильно спроєктована архітектура з MVVM + Clean дає в 3 рази менше регресій порівняно з монолітним ViewController. А витрати на її впровадження окупаються за 2–3 спринти. Середня економія на усуненні дефектів після впровадження — до 40% часу, що для команди з п'яти осіб може означати понад 10 000 доларів на рік.
Згідно з рекомендаціями Apple щодо проєктування додатків, розділення обов'язків — ключовий фактор стійкості коду.
Чому варто довірити архітектуру професіоналам?
Неправильний вибір паттерну на старті веде до переписування половини коду через рік. Ми бачили десятки проєктів, де спроба заощадити на архітектурі оберталася багатомісячним рефакторингом. У нас за плечима 10+ років комерційної розробки, досвід роботи з додатками від 1 до 50 розробників, понад 100 успішно реалізованих проєктів. Ми допомагаємо уникнути типових помилок:
- Overengineering для простого MVP (призначаємо MVVM, а не VIPER).
- Відсутність dependency injection — підключаємо Hilt/Koin/Dagger вже на старті.
- Ігнорування тестованості — закладаємо протоколи/інтерфейси з першого коміту.
Зв'яжіться з нами через Telegram або email — отримайте безкоштовну оцінку вашого проєкту та рекомендацію щодо оптимальної архітектури. Замовте проєктування вже сьогодні — і за тиждень стартуйте з чистим, масштабованим кодом.
Додаткова таблиця: порівняння витрат на впровадження
| Паттерн |
Час проєктування |
Кількість файлів на 1 екран |
Час написання тестів |
| MVVM |
2–3 дні |
5–7 |
1 день |
| MVVM + Clean |
4–5 днів |
10–12 |
2 дні |
| BLoC |
3–4 дні |
6–8 |
1.5 дня |
| VIPER |
5–7 днів |
12–15 |
2.5 дня |
| TCA |
5–6 днів |
8–10 |
2 дні |
Час вказано для команди з 2–3 розробників. З нашим шаблоном (generator) стартовий каркас готовий за 1 день незалежно від обраного паттерну.