Міграція мобільного додатку на нову версію Flutter SDK
Flutter змінює публічний API частіше, ніж здається. Переход з Flutter 2.x на 3.x привніс null safety як обов'язкову вимогу, відмову від WidgetsFlutterBinding.ensureInitialized() в деяких випадках, новий Navigator 2.0 та зміни MaterialState → WidgetState. Міграція виробничого додатку—це не просто flutter pub upgrade.
Що реально ломається при мажорному апгрейді
Null Safety: за межами dart migrate
Інструмент dart migrate робить 70–80% роботи—проставляє ? та ! там, де аналізатор може вивести nullability. Решта 20–30%—ручна робота, і саме там приховані баги.
Типичний випадок: плагін із pub.dev не перейшов на null safety та заморожений. Варіанти: fork із патчем, заміна на альтернативу, написання обгортки. Із 40+ залежностями це перетворюється на тиждень роботи тільки на залежностях.
Ще небезпечніше—код, який проходить міграцію без помилок, але змінює поведінку. late змінна без ініціалізації викидає LateInitializationError в рантаймі там, де раніше був просто null. Ловиться тільки тестами або в продакшені.
Breaking Changes в Material 3
Flutter 3.16+ переключив useMaterial3: true за замовчуванням. Якщо в додатку кастомна тема через ThemeData, частина компонентів виглядає інакше: змінилися розміри кнопок, відступи в AppBar, ієрархія TextTheme. Додатки, відкинені через зовнішні зміни після апгрейду—не рідкість.
Рішення: явно встановити useMaterial3: false на час міграції, потім поетапно переходити на M3 за компонентом.
Зміни в Navigator та маршрутизації
Якщо додаток використовує go_router, кожен мажорний релiз пакету ломає API по-новому. Переход з go_router 6.x на 10.x—це повний рерайт конфігурації: GoRoute + ShellRoute замість вкладених GoRoute, зміни в сигнатурі redirect callback, новий StatefulShellRoute для persistent navigation.
Зміни в рендеринзі: Impeller
Flutter 3.10+ включив Impeller за замовчуванням на iOS (Android—опціонально). Impeller забирає jank від компіляції шейдерів, але ломає кастомні CustomPainter реалізації, що використовують нестандартні BlendMode або ImageFilter. Після включення Impeller всі анімації та кастомні віджети потребують тестування на реальних пристроях.
Процес міграції
Аудит залежностей—перший крок. flutter pub outdated показує, що застаріло, але не показує breaking changes. Стежимо CHANGELOG кожного пакету вручну для мажорних версій. Ділимо залежності на три категорії:
- оновлюються без змін коду
- потребують змін коду (API changes)
- немає сумісної версії—потрібна заміна або fork
Feature-flag підхід для крупних додатків. Створюємо migration гілку, оновлюємо SDK та пакети, фіксуємо всі помилки компіляції. Потім фіксуємо поетапно, починаючи з core-шару (моделі, репозиторії) та закінчуючи UI.
Тестування після міграції:
-
flutter analyze—без попереджень статичного аналізу -
flutter test—весь существуючий suite повинен проходити - Golden тести для UI-компонентів (якщо використовуються)—перегенеруємо, Impeller рендерить пікселі інакше
- Smoke-тест на фізичних пристроях: iOS + Android, бюджетний Android обов'язковий
Конкретні проблеми з практики
У проекті (e-commerce, Flutter 2.8 → 3.19) головна складність була не в коді, а в плагіні flutter_local_notifications. Між версіями 9.x та 16.x змінилася весь Android-сайд: новий FlutterLocalNotificationsPlugin.initialize() з InitializationSettings, обов'язковий onDidReceiveNotificationResponse замість deprecated колбека. Плюс Android 13 потребує явного POST_NOTIFICATIONS permission—без нього мовчки не працює.
Інший випадок—image_picker після оновлення почав повертати XFile замість File. Везде в коді File(imagePicker.path) замінювали на File(xFile.path)—механічно, але їх було 23 використання в різних екранах.
Тривалість
| Масштаб додатку | Типична тривалість міграції |
|---|---|
| Small (< 20 екранів, < 15 залежностей) | 3–5 днів |
| Medium (20–60 екранів, 15–40 залежностей) | 1–3 тижні |
| Large (60+ екранів, складна архітектура) | 3–6 тижнів |
Вартість розраховується індивідуально після аудиту репозиторію та прегляду списку залежностей.







