Route — to abstrakcyjna klasa we Flutter, reprezentująca osobny ekran lub stronę w historii nawigacji Navigator. Konkretne implementacje Route — MaterialPageRoute, CupertinoPageRoute i PlatformRoute — określają, jak ekran jest wyświetlany i jaka animacja jest używana przy przejściu. W przeciwieństwie do zwykłego widgetu, Route ma własny cykl życia z metodami didPush, didPop, didReplace i didChangeNext. Według Flutter API Reference (2026), każdy Route zarządza ModalBarrier, uwzględnia cechy platformy (przesunięcie wstecz na iOS) i zapewnia izolację stanu między ekranami.
Najważniejsze
Route — to podstawa systemu nawigacji Flutter. Każdy ekran, który użytkownik widzi w aplikacji, jest reprezentowany przez obiekt Route znajdujący się w stosie Navigator. Route abstrahuje ekran od kodu zarządzającego: Navigator dodaje i usuwa Route, a wewnątrz Route znajduje się interfejs widoczny dla użytkownika. Taka architektura rozdziela odpowiedzialność między nawigacją a wyświetlaniem.
W przeciwieństwie do zwykłej zamiany widgetów, Route zapewnia usługi niedostępne dla zwykłych widgetów: ModalBarrier (przyciemnienie tła przy otwartym oknie dialogowym), zarządzanie animacją wejścia i wyjścia, obsługę sprzętowego przycisku „Wstecz” na Androidzie oraz integrację z animacją Hero dla płynnych przejść między ekranami.
Według Flutter Cookbook (2026), Route jest kluczowym elementem animacji Hero: widget Hero na jednym Route automatycznie animuje przejście do widgetu Hero na następnym Route, tworząc efekt „lecącego” elementu. Jest to możliwe właśnie dlatego, że Route przechowuje oba ekrany w Overlay podczas animacji.
Route jest podstawą zarówno dla prostych aplikacji mobilnych (przez Navigator 1.0), jak i dla złożonych scenariuszy z głębokimi linkami (Navigator 2.0). W Navigator 2.0 Route reprezentuje Page, który RouterDelegate konwertuje z konfiguracji marszruty. Zatem zrozumienie Route jest niezbędne do pracy z dowolnym systemem nawigacji Flutter — niezależnie od wybranego podejścia lub pakietu.
Flutter udostępnia kilka wbudowanych implementacji Route, z których każda dostosowuje zachowanie do konkretnej platformy. Wybór odpowiedniego typu Route wpływa na doświadczenie użytkownika: animacja Material na Androidzie i animacja Cupertino na iOS tworzą wrażenie „natywnej” aplikacji.
| Typ Route | Animacja | Platforma | Cechy |
|---|---|---|---|
| MaterialPageRoute | Przesunięcie z dołu do góry | Android, desktop | cień przy przejściu, automatyczna obsługa SafeArea |
| CupertinoPageRoute | Przesunięcie z prawej do lewej | iOS, iPadOS | gest przesunięcia wstecz, przezroczyste tło przy przejściu |
| PlatformRoute | Automatyczny wybór | Wszystkie platformy | wybiera typ na podstawie TargetPlatform |
| PageRouteBuilder | Niestandardowa | Wszystkie platformy | pełna kontrola nad animacją przez AnimationController |
MaterialPageRoute — to najczęściej używana implementacja Route. Animuje wejście nowego ekranu od dołu do góry ze stopniowym pojawianiem się. Przy wyjściu ekran animuje się z góry na dół, wracając do pozycji początkowej. Panel narzędzi (AppBar) i treść ekranu animują się osobno, tworząc efekt hierarchii.
CupertinoPageRoute imituje UINavigationController z iOS. Nowy ekran wjeżdża z prawej strony, zakrywając poprzedni. Kluczową cechą jest obsługa interaktywnego gestu przesunięcia wstecz, zaimplementowana przez CupertinoBackGestureDetector. Ten gest jest obsługiwany nawet w środku animacji, zapewniając naturalne zachowanie znane użytkownikom iPhone.
Route ma własny cykl życia, który różni się od cyklu życia zwykłego StatefulWidget. Zrozumienie tego cyklu jest niezbędne do prawidłowej inicjalizacji danych, subskrypcji strumieni i zwalniania zasobów przy zamykaniu ekranu.
Cykl życia Route składa się z czterech głównych etapów. Transition — Route jest tworzony i animowany przy wejściu (wywołuje didPush). Active — Route jest w pełni wyświetlany i interaktywny dla użytkownika. Inactive — inny Route zasłania bieżący (dialog, dolny ekran), ale Route pozostaje w stosie. Disposed — Route jest usuwany ze stosu i niszczony, wywoływane są didPop i dispose.
Metody cyklu życia Route mogą być nadpisane w niestandardowej implementacji. Na przykład didPop jest wywoływany, gdy Route jest usuwany ze stosu — tutaj można zapisać wersję roboczą danych. didChangeNext jest wywoływany, gdy następny Route w stosie się zmienił — przydatne do aktualizacji UI przy zmianie historii nawigacji.
Według Flutter API Route.didPop (2026), ważne jest, aby nie mylić cyklu życia Route z cyklem życia State wewnątrz Route. StatefulWidget wewnątrz Route ma własne initState i dispose, które są wywoływane odpowiednio na etapach Transition i Disposed. Route żyje dłużej niż jego wewnętrzny State — Route pozostaje w Overlay nawet gdy jego widgety są tymczasowo ukryte przez inny Route.
Route zapewnia mechanizmy do przesyłania danych zarówno na wejściu (przy tworzeniu), jak i na wyjściu (przy zakończeniu). Prawidłowe przesyłanie danych przez Route eliminuje potrzebę globalnych zmiennych i InheritedWidget, czyniąc nawigację bezpieczną typowo i przewidywalną.
Do przesyłania danych na nowy ekran używany jest konstruktor odbierającego widgetu lub argument arguments w Navigator.pushNamed. Wewnątrz Route dane są dostępne przez RouteSettings.arguments, który jest przechowywany w obiekcie Route. To podejście działa dla wszystkich typów Route — MaterialPageRoute, CupertinoPageRoute i niestandardowych implementacji.
Do zwracania danych używany jest drugi argument Navigator.pop(context, result). Navigator.push zwraca Future
Przy bezpośrednim wywołaniu Navigator.push z MaterialPageRoute dane są przesyłane przez konstruktor docelowego ekranu. MethodChannel nie jest używany — to czysta interakcja Dart. Takie podejście jest preferowane do typowo bezpiecznego przesyłania złożonych obiektów.
Rozważmy przykład tworzenia niestandardowego Route z własną animacją i przesyłaniem danych. PageRouteBuilder pozwala zdefiniować animację wejścia i wyjścia z pełną kontrolą nad krzywą animacji i czasem trwania.
// Niestandardowy Route z animacją przesunięcia
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),
));
// Zwróć dane z ekranu
ElevatedButton(
onPressed: () => Navigator.of(context).pop({'selected': true, 'id': '42'}),
child: const Text('Wybierz'),
);
// Pobierz wynik na ekranie wywołującym
final result = await Navigator.push(context, MaterialPageRoute(
builder: (context) => const SelectionPage(),
));
if (result != null) {
print('Wybrano: ${result['selected']}');
}
W przykładzie PageRouteBuilder definiuje niestandardową animację przesunięcia z dołu z przezroczystością. transitionDuration określa prędkość animacji. Kod demonstruje również przesyłanie wyniku: ekran szczegółów zwraca Map z wyborem użytkownika, a ekran wywołujący otrzymuje te dane przez Future z push. Route zapewnia pełną izolację: wersje robocze na ekranie szczegółów nie wpływają na stan listy.
Często zadawane pytania
Route — to obiekt zarządzający ekranem na poziomie nawigacji: przechowuje animację, ModalBarrier i cykl życia. Widget — to opis części interfejsu. Route zawiera Widget wewnątrz siebie, ale także zapewnia usługi (warstwa Overlay, animacja Hero) niedostępne dla zwykłych widgetów. Jeden Route może zawierać złożoną hierarchię widgetów o dowolnej głębokości.
Użyj PageRouteBuilder z parametrami pageBuilder (budowa ekranu) i transitionsBuilder (definicja animacji). W transitionsBuilder dostępne są animation (0.0–1.0) i secondaryAnimation dla równoległych animacji. Dla pełnej kontroli utwórz podklasę Route i nadpisz buildPage, createAnimationController i buildTransitions, co da dostęp do niskopoziomowych AnimationController.
Do przesyłania złożonych obiektów użyj konstruktora docelowego ekranu przy bezpośrednim Navigator.push lub argumentu arguments przy pushNamed. Upewnij się, że obiekt jest serializowalny (Map, JSON lub niestandardowa klasa). Do typowo bezpiecznego przesyłania we Flutter używane są modele freezed lub json_serializable, które gwarantują poprawną deserializację przy przesyłaniu przez RouteSettings.
Route.dispose nie jest wywoływane, jeśli Route pozostaje w stosie Navigator. Na przykład przy otwieraniu nowego Route stary Route przechodzi w stan nieaktywny (Inactive), ale nie jest niszczony — pozostaje w stosie do szybkiego powrotu. Dispose jest wywoływane tylko przy usunięciu Route ze stosu przez pop, pushReplacement lub pushAndRemoveUntil. Do zwalniania zasobów używaj dispose State wewnątrz Route, a nie dispose samego Route.
Użyj ModalRoute.of(context) do pobrania bieżącego Route z BuildContext. Właściwość ModalRoute.isActive pokazuje, czy Route jest aktualnie widocznym ekranem. ModalRoute.isCurrent — true, jeśli Route jest na szczycie stosu. Do obserwowania zmian stosu subskrybuj obserwatorów Navigator przez RouteAware i RouteObserver, które powiadamiają o zmianie aktywnego Route.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również