Міграція з нативу на Flutter без заморозки фіч
Павло Рубановський
· 9 хв читання

Щоб перенести нативний застосунок для iOS та Android на Flutter без заморозки фіч, побудуйте Flutter-версію ядра застосунку як компактний MVP, поки нативні застосунки й далі виходять з оновленнями. Коли ядро готове, опублікуйте його як оновлення в тих самих лістингах (сторінках застосунку в сторах), розгорніть поступово, а тоді перенесіть решту функцій — починаючи з найпопулярніших.
Навіщо переводити два нативні застосунки на Flutter?
Бо одна кодова база й одна команда обходяться дешевше, ніж дві. Коли застосунки окремі, на Swift і Kotlin, кожну функцію будують, тестують і виправляють двічі, а самі застосунки розходяться: на iOS з’являється сценарій (флоу), якого немає на Android, баг виправляють лише з одного боку. Flutter збирає обидва застосунки з однієї кодової бази на Dart, тож функцію будують один раз, і вона виходить в обох сторах. Якщо ви ще обираєте стек, а не йдете з нього, фреймворки порівняно в статті Flutter, натив чи React Native.
Коли мігрувати не варто?
Коли перехід коштує більше, ніж заощадить одна кодова база. Міграція перебудовує все, що бачить користувач, тож вона має окупитися.
| Ситуація | Мігрувати? | Чому |
|---|---|---|
| Два застосунки, один продукт, кожна функція — двічі | Так | Саме цю проблему й розв’язує Flutter |
| Переважно важка нативна робота: власний конвеєр камери, складна фонова обробка, глибокий доступ до апаратного забезпечення | Зазвичай ні | Більшість коду однаково лишиться нативною; ви отримаєте міст, а не простоту |
| Застосунок скоро виведуть з експлуатації чи замінять | Ні | Нова кодова база не проживе достатньо, щоб окупитися |
| Невеликий застосунок на кілька екранів | Не як поетапний проєкт | Перебудуйте його за один раз; «спершу ядро» тут мало що дає |
Як працює підхід «спершу ядро», крок за кроком?
Flutter-застосунок будують поруч із нативними, а не всередині них, і нативні застосунки лишаються робочим продуктом, доки він не готовий.
1. Огляд нативних застосунків. Читаємо обидві кодові бази й складаємо список усіх SDK та нативних інтеграцій із тим, що кожній потрібно у Flutter. Результат — письмовий обсяг робіт і фіксована ціна за кожен модуль. 2. Узгодження ядра. Вхід в акаунт, основні сценарії, заради яких застосунок відкривають, оплати й налаштування, що впливають на дані користувача. Решту визначає аналітика: усе, чим щотижня користується значна частина аудиторії, іде в ядро, бо перехід не має цього відібрати. 3. Ядро на Flutter — у вашому репозиторії. Код лежить у вашому репозиторії з першого дня, а регулярні тестові білди з TestFlight і Google Play ви встановлюєте на власний телефон. 4. Нативні застосунки й далі виходять. Виправлення й нові функції виходять із нативних кодових баз як завжди. 5. Перехід. Flutter-застосунок публікується з ваших акаунтів у сторах як оновлення наявних застосунків і розгортається поступово. 6. Перенесення решти. Функції, що не ввійшли в ядро, виходять звичайними оновленнями — спершу найпопулярніші. Відтоді кожну функцію будують один раз.
Що відбувається з новими функціями, поки будується Flutter-ядро?
Нативні застосунки й далі випускають оновлення — саме для цього Flutter-версію будують паралельно. Заморозки фіч немає, але є одне правило: зміна в сценарії, що входить до ядра, має потрапити й у Flutter, інакше ядро застаріє в день релізу.
| Зміна під час розробки | Нативні застосунки | Flutter-застосунок |
|---|---|---|
| Виправлення бага | Виходить як завжди | Перевіряємо той самий сценарій у Flutter |
| Нова функція в сценарії, що входить до ядра | Виходить як завжди | Додається в ядро до переходу |
| Нова функція поза ядром | Виходить як завжди | Переноситься після переходу |
| Велика функція, яку ще не почали | На ваш вибір | Часто дешевше збудувати один раз, у Flutter |
Останній рядок — справжній компроміс: збудувати велику функцію двічі (зараз нативно, потім у Flutter) або один раз — у Flutter. Це продуктове рішення, і воно має дійти до вас із ціною кожного варіанта.
Чому не мігрувати екран за екраном усередині нативного застосунку?
Flutter можна вбудувати в наявний застосунок для iOS чи Android як модуль — це називається add-to-app, — і деякі команди мігрують саме так, по одному сценарію. Ми — ні. Увесь перехідний період доводиться підтримувати два набори інструментів збірки (тулчейни), дві системи збірки й міст, через який нативні та Flutter-екрани ділять навігацію, стан і сесію користувача. До того ж документація Flutter перелічує обмеження такого підходу на мобільних платформах — наприклад, плагіни, які розраховують, що Flutter-activity є завжди.
Зі «спершу ядро» мосту немає: до переходу — нативний застосунок, після — Flutter.
Як акаунти й дані користувачів переходять у Flutter-застосунок?
Акаунти на бекенді мігрувати не треба: Flutter-застосунок працює з тим самим API. Робота потрібна з тим, що нативний застосунок зберіг на телефоні. Простіше кажучи, це те, хто ввійшов в акаунт, налаштування користувача й дані, збережені для роботи без мережі. Flutter-застосунок має знайти все це під час першого запуску, інакше користувачі помітять різницю в день оновлення.
- Сесії. Якщо Flutter-застосунок не зможе прочитати токени, які нативний зберіг у захищеному сховищі (Keychain на iOS, зашифроване сховище на Android), у день оновлення з акаунтів вилетять усі користувачі. Під час першого запуску Flutter-застосунок зчитує їх і переносить сесію.
- Налаштування. Плагін
shared_preferencesу Flutter працює поверх NSUserDefaults і Android SharedPreferences, але за замовчуванням читає лише ключі з префіксомflutter.; у його документації описано, як дістатися до налаштувань, які записала не-Flutter версія застосунку. - Локальні бази даних і файли. Core Data (iOS) і Room (Android) — вбудовані способи, якими нативні застосунки зберігають базу даних на телефоні. Їхні дані, звичайні бази SQLite й файли, записані нативним застосунком, потребують одноразової міграції під час першого запуску.
Усе це тестуємо, встановлюючи Flutter-білд поверх нативного застосунку на реальних пристроях, — саме цей шлях пройде кожен наявний користувач.
А як щодо оплат, Bluetooth та інших нативних інтеграцій?
Вони переходять разом із застосунком. Для поширених SDK є Flutter-плагіни. Якщо плагіна немає або нативний код варто зберегти, Flutter викликає його через platform channels — асинхронні повідомлення між Dart і Kotlin чи Java на Android та Swift чи Objective-C на iOS. Нативний код для власного SDK часто можна просто лишити за каналом. Віджети на головному екрані, застосунки для годинника й розширення пишуть нативно навіть у Flutter-застосунку, тож в огляді їх оцінюють окремо.
Чи збереже Flutter-застосунок лістинг і відгуки?
Так, якщо його опубліковано як оновлення того самого застосунку, а не як новий. Стори впізнають застосунок за ідентифікатором, а не за мовою, якою його написано.
- Google Play. Application ID ідентифікує застосунок у Google Play; якщо його змінити, Play вважатиме завантаження іншим застосунком (Android Developers: application ID). Крім того, оновлення має бути підписане тим самим ключем підпису застосунку (підпис застосунку).
- App Store. Нова версія створюється в тому самому записі застосунку, що й попередня (App Store Connect Help), а bundle ID не можна змінити після завантаження першого білда (інформація про застосунок).
Тож Flutter-застосунок зберігає bundle ID та application ID нативного, підписується тим самим ключем і приходить до користувачів як звичайне оновлення в тому самому лістингу. Загальний рейтинг в App Store скидається, лише якщо ви самі це оберете, а письмові відгуки показуються в будь-якому разі (Apple: скидання рейтингу).
Одна практична деталь: Google Play вимагає більшого versionCode для кожного релізу й відхиляє вже використаний (версіонування). Нативний застосунок продовжує його збільшувати, поки будується ядро, тож номер білда Flutter-версії має починатися вище за останній реліз нативної.
Що відбувається в день переходу?
Спершу Flutter-застосунок отримує невелика частка користувачів. Інструменти сторів станом на жовтень 2026:
| Стор | Інструмент | Як працює |
|---|---|---|
| App Store | Phased release | Протягом 7 днів, від 1% до 100% користувачів з автооновленнями; можна призупинити до 30 днів |
| Google Play | Staged rollout | Відсоток обираєте й підвищуєте ви самі; працює лише для оновлень, а перехід — саме оновлення |
Два застереження. Під час поетапного випуску (Phased release) будь-хто може завантажити нову версію з App Store вручну. У Google Play зупинка розгортання припиняє видачу версії новим користувачам, але ті, хто вже її отримав, на ній і лишаються; вихід — новий реліз.
План відкату узгоджують заздалегідь. Найшвидше виправлення — зазвичай новий Flutter-білд; повторна публікація нативного білда з вищим номером версії — крайній захід, і він спрацює, лише якщо нативний застосунок прочитає те, що встиг зберегти Flutter.
Що буде з нативною командою?
Вона випускає оновлення аж до переходу. Після нього у Flutter-застосунку однаково лишається нативна робота: platform channels, інтеграції SDK, віджети, застосунки для годинника. Нативні розробники можуть вивчити Dart, а їхнього знання платформ Flutter-командам часто бракує. Перенавчити команду, перевести на інші завдання чи скоротити — вирішувати вам; у плані для цього має стояти дата.
Які ризики й як їх зменшити?
| Ризик | Як зменшити |
|---|---|
| У ядрі бракує того, на що покладаються користувачі | Визначати ядро за аналітикою, а не з пам’яті |
| Користувачів викидає з акаунтів або губляться локальні дані | Тестувати встановлення поверх нативного застосунку на реальних пристроях |
| Для нативного SDK немає Flutter-плагіна | Знайти це на огляді, ще до ціни; закласти platform channel |
| Ядро розростається й ніяк не виходить | Фіксований обсяг на модуль; функції поза ядром переносяться після переходу |
Перенесення ваших нативних застосунків на Flutter разом із нами
DriveCode розробляє застосунки для iOS та Android, Flutter-first. Ми починаємо з огляду обох нативних застосунків і письмово фіксуємо обсяг: ядро, нативні інтеграції та як переходять дані користувачів. Ви отримуєте фіксовану ціну за кожен модуль, код лежить у вашому репозиторії з першого дня, а Flutter-застосунок виходить із ваших власних акаунтів у сторах. Як це працює: міграція з нативу на Flutter без заморозки фіч. Якщо команда, що робила нативні застосунки, уже пішла, спершу — підхоплення застосунку. Скільки коштує міграція і чому: вартість міграції на Flutter.
Що часто питають
Чи треба зупиняти релізи нативних застосунків на час міграції? Ні. Flutter-ядро виходить як MVP, поки нативні застосунки й далі випускають оновлення; решта функцій виходить у регулярних оновленнях, спершу найпопулярніші. Зміни в сценаріях, що входять до ядра, паралельно додаються й до обсягу Flutter.
Чи доведеться користувачам завантажувати новий застосунок? Ні. Flutter-застосунок зберігає Bundle ID, application ID і ключ підпису, тож приходить як звичайне оновлення в тому самому лістингу.
Ви мігруєте екран за екраном через add-to-app? Ні. Ми будуємо ядро як окремий Flutter-застосунок і перемикаємося, коли воно готове, тож підтримувати міст між нативними й Flutter-екранами не доводиться.
Чи можна відкотитися, якщо щось піде не так? Не однією кнопкою. Інструменти сторів призупиняють або зупиняють розгортання, але не повертають користувачів на попередню версію. Саме тому перехід розгортають поступово.
Скільки коштує міграція? Залежить від розміру ядра й нативних інтеграцій, тому фіксовану ціну за кожен модуль ми називаємо після огляду обох застосунків. Проєкти — від $10K.