Route — це абстрактний клас у Flutter, що представляє окремий екран або сторінку в навігаційній історії Navigator. Конкретні реалізації Route — MaterialPageRoute, CupertinoPageRoute та PlatformRoute — визначають, як екран відображається та яка анімація використовується під час переходу. На відміну від звичайного віджета, Route має власний життєвий цикл з методами didPush, didPop, didReplace та didChangeNext. Згідно з Flutter API Reference (2026), кожен Route керує ModalBarrier, враховує платформні особливості (свайп-назад на iOS) та забезпечує ізоляцію стану між екранами.
Головне
Route — це основа навігаційної системи Flutter. Кожен екран, який користувач бачить у застосунку, представлений об'єктом Route, що знаходиться в стеку Navigator. Route абстрагує екран від керуючого коду: Navigator додає та видаляє Route, а всередині Route знаходиться інтерфейс, який бачить користувач. Така архітектура розділяє відповідальність між навігацією та відображенням.
На відміну від простої заміни віджетів, Route надає сервіси, недоступні звичайним віджетам: ModalBarrier (затінення фону при відкритому діалозі), управління анімацією входу та виходу, обробка апаратної кнопки «Назад» на Android та інтеграція з Hero-анімацією для плавних переходів між екранами.
Згідно з Flutter Cookbook (2026), Route є ключовим елементом для Hero-анімації: Hero-віджет на одному Route автоматично анімує перехід до Hero-віджета на наступному Route, створюючи ефект «літаючого» елемента. Це можливо саме тому, що Route зберігає обидва екрани в Overlay під час анімації.
Route є основою як для простих мобільних застосунків (через Navigator 1.0), так і для складних сценаріїв з глибокими посиланнями (Navigator 2.0). У Navigator 2.0 Route представляє Page, яку RouterDelegate конвертує з конфігурації маршруту. Таким чином, розуміння Route необхідне для роботи з будь-якою системою навігації Flutter — незалежно від обраного підходу або пакета.
Flutter надає кілька вбудованих реалізацій Route, кожна з яких адаптує поведінку під конкретну платформу. Вибір правильного типу Route впливає на користувацький досвід: Material-анімація на Android та Cupertino-анімація на iOS створюють відчуття «рідного» застосунку.
| Тип Route | Анімація | Платформа | Особливості |
|---|---|---|---|
| MaterialPageRoute | Слайд знизу вгору | Android, десктоп | Тінь при переході, автоматична обробка SafeArea |
| CupertinoPageRoute | Слайд справа наліво | iOS, iPadOS | Свайп-назад жестом, прозорий фон при переході |
| PlatformRoute | Автоматичний вибір | Всі платформи | Вибирає тип на основі TargetPlatform |
| PageRouteBuilder | Кастомна | Всі платформи | Повний контроль над анімацією через AnimationController |
MaterialPageRoute — найбільш часто використовувана реалізація Route. Вона анімує вхід нового екрана знизу вгору з поступовою появою. При виході екран анімується зверху вниз, повертаючись у вихідне положення. Панель інструментів (AppBar) та тіло екрана анімуються окремо, створюючи ефект ієрархії.
CupertinoPageRoute імітує UINavigationController з iOS. Новий екран в'їжджає справа, закриваючи попередній. Ключова особливість — підтримка інтерактивного свайп-назад жестом, яка реалізована через CupertinoBackGestureDetector. Цей жест обробляється навіть у середині анімації, забезпечуючи природну поведінку, знайому користувачам iPhone.
Route має власний життєвий цикл, який відрізняється від життєвого циклу звичайного StatefulWidget. Розуміння цього циклу необхідне для правильної ініціалізації даних, підписки на стріми та звільнення ресурсів при закритті екрана.
Життєвий цикл Route складається з чотирьох основних стадій. Transition — Route створюється та анімується при вході (викликається didPush). Active — Route повністю відображається та взаємодіє з користувачем. Inactive — інший Route перекриває поточний (діалог, нижній екран), але Route залишається в стеку. Disposed — Route видаляється зі стеку та знищується, викликаються didPop та dispose.
Методи життєвого циклу Route можуть бути перевизначені в кастомній реалізації. Наприклад, didPop викликається, коли Route видаляється зі стеку — тут можна зберегти чернетку даних. didChangeNext викликається, коли наступний Route у стеку змінився — корисно для оновлення UI при зміні історії навігації.
Згідно з Flutter API Route.didPop (2026), важливо не плутати життєвий цикл Route з життєвим циклом State всередині Route. StatefulWidget всередині Route має власний initState та dispose, які викликаються в рамках стадії Transition та Disposed відповідно. Route живе довше, ніж його внутрішній State — Route залишається в Overlay навіть коли його віджети тимчасово приховані іншим Route.
Route надає механізми для передачі даних як на вхід (при створенні), так і на вихід (при завершенні). Правильна передача даних через Route позбавляє від глобальних змінних та InheritedWidget, роблячи навігацію типобезпечною та передбачуваною.
Для передачі даних на новий екран використовується конструктор приймаючого віджета або аргумент arguments у Navigator.pushNamed. Всередині Route дані доступні через RouteSettings.arguments, який зберігається в об'єкті Route. Цей підхід працює для всіх типів Route — MaterialPageRoute, CupertinoPageRoute та кастомних реалізацій.
Для повернення даних використовується другий аргумент Navigator.pop(context, result). Navigator.push повертає Future<Т>, яка завершується зі значенням, переданим у pop. Якщо pop викликаний без аргументу, Future завершується з null. Цей механізм — аналог startActivityForResult в Android та completion handler в iOS, але реалізований через Dart Futures.
При прямому виклику Navigator.push з MaterialPageRoute дані передаються через конструктор цільового екрана. MethodChannel не використовується — це чиста Dart-взаємодія. Такий підхід є кращим для типобезпечної передачі складних об'єктів.
Розглянемо приклад створення кастомного Route з власною анімацією та передачею даних. PageRouteBuilder дозволяє визначити анімацію входу та виходу з повним контролем над кривою анімації та тривалістю.
// Кастомний Route з анімацією слайда
Navigator.push(context, PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) {
return DetailPage(productId: '42');
},
transitionsBuilder: (context, animation, secondaryAnimation, child) {
const begin = Offset(0.0, 0.3);
const end = Offset.zero;
final tween = Tween(begin: begin, end: end);
final offsetAnimation = animation.drive(tween);
return SlideTransition(position: offsetAnimation, child: child);
},
transitionDuration: const Duration(milliseconds: 400),
));
// Повернути дані з екрана
ElevatedButton(
onPressed: () => Navigator.of(context).pop({'selected': true, 'id': '42'}),
child: const Text('Вибрати'),
);
// Отримати результат на викликаючому екрані
final result = await Navigator.push(context, MaterialPageRoute(
builder: (context) => const SelectionPage(),
));
if (result != null) {
print('Вибрано: ${result['selected']}');
}
У прикладі PageRouteBuilder визначає кастомну анімацію слайда знизу з прозорістю. transitionDuration задає швидкість анімації. Код також демонструє передачу результату: детальний екран повертає Map з вибором користувача, а викликаючий екран отримує ці дані через Future від push. Route забезпечує повну ізоляцію: чернетки на детальному екрані не впливають на стан списку.
Часті запитання
Route — це об'єкт, що керує екраном на рівні навігації: він зберігає анімацію, ModalBarrier та життєвий цикл. Widget — це опис частини інтерфейсу. Route містить Widget всередині себе, але також надає сервіси (Overlay-шар, Hero-анімація), недоступні звичайним віджетам. Один Route може містити складну ієрархію віджетів будь-якої глибини.
Використовуйте PageRouteBuilder з параметрами pageBuilder (побудова екрана) та transitionsBuilder (визначення анімації). У transitionsBuilder доступні animation (0.0–1.0) та secondaryAnimation для паралельних анімацій. Для повного контролю створіть підклас Route та перевизначте buildPage, createAnimationController і buildTransitions, що дасть доступ до низькорівневих AnimationController.
Для передачі складних об'єктів використовуйте конструктор цільового екрана при прямому Navigator.push або аргумент arguments при pushNamed. Переконайтеся, що об'єкт серіалізований (Map, JSON або кастомний клас). Для типобезпечної передачі у Flutter використовуються freezed або json_serializable моделі, які гарантують коректну десеріалізацію при передачі через RouteSettings.
Route.dispose не викликається, якщо Route залишається в стеку Navigator. Наприклад, при відкритті нового Route старий Route переходить у неактивний стан, але не знищується — він залишається в стеку для швидкого повернення. Dispose викликається тільки при видаленні Route зі стеку через pop, pushReplacement або pushAndRemoveUntil. Для звільнення ресурсів використовуйте dispose State всередині Route, а не dispose самого Route.
Використовуйте ModalRoute.of(context) для отримання поточного Route з BuildContext. Властивість ModalRoute.isActive показує, чи є Route поточним видимим екраном. ModalRoute.isCurrent — true, якщо Route є вершиною стеку. Для спостереження за змінами стеку підпишіться на спостерігачів Navigator через RouteAware та RouteObserver, які сповіщають про зміну активного Route.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також