Mobilni razvoj
Fluttergo_routerDuboke povezniceNavigacijaRazvoj mobilnih aplikacijaWeb

Flutter navigacija s go_router: duboke poveznice, auth guardovi, ugniježđene rute i podrška za web

AO
Adrijan Omićević
·13 min čitanja

# Što ćete izgraditi i zašto je to važno#

Ovaj vodič prikazuje navigacijsku postavu spremnu za produkciju koristeći go_router s deep linkovima, auth guardovima, ugniježđenim rutama, ShellRoute layoutom i prvoklasnom podrškom za web. Ciljana ključna riječ, Flutter go_router deep links, više je od marketinške fraze: deep linkovi vas prisiljavaju da navigaciju tretirate kao arhitekturni problem, a ne UI detalj.

U stvarnoj aplikaciji rute nisu samo ekrani. One kodiraju dozvole, onboarding, granice značajki, analitiku i stanje vođeno URL-om poput tabova, filtera i odabranih entiteta.

Ako planirate universal linkove i app linkove end-to-end, pročitajte naš poseban pregled deep linkinga: Flutter deep linking: universal links, app links, routing.

# Preduvjeti#

ZahtjevVerzijaNapomene
Flutter3.22+Podržani web i mobilne platforme
Dart3.4+Odgovara trenutnom Flutter stable
go_router14+API je usklađen sa ShellRoute i imenovanim rutama
Rješenje za stanjeBilo kojeRiverpod, Bloc, Provider, itd.
Postavke deep linkovaiOS i AndroidAssociated domains i intent filteri

Dizajn routinga snažno je povezan sa strukturom projekta. Ako vaš codebase raste, razmotrite feature-first raspored i eksplicitne granice: Flutter app architecture: clean architecture, feature-first.

# Koncepti routinga koji stvarno znače u produkciji#

Deklarativne rute s determinističkim URL-ovima#

Deep linking zahtijeva da se svaki bitan ekran može prikazati kao URL lokacija. Ako se do UI-ja može doći samo navigacijom u memoriji, nije moguće linkati, dijeliti i teže ga je testirati.

Dobro pravilo je: ako korisnik može doći na nešto iz notifikacije, e-maila, QR koda ili preglednika, treba stabilnu rutu i predvidljivu priču o restoreu.

Redirecti nisu samo provjere autentikacije#

Logika preusmjeravanja obično evoluira od jednostavne „login rampe” u policy engine:

  • onboarding dovršen naspram nedovršen
  • ekrani za prisilnu nadogradnju
  • maintenance mode
  • pristup temeljen na rolama
  • feature flagovi i A B eksperimenti
  • rukovanje isteklih sesija pri cold startu kroz deep link

go_router može sve to odraditi, ali samo ako redirecte dizajnirate kao deterministička pravila s minimalnim nuspojavama.

Ugniježđena navigacija je UX zahtjev, a ne stil koda#

Donja navigacija na mobilnim aplikacijama obično zahtijeva neovisne navigacijske stackove po tabu. Ako to ne napravite, korisnici gube povijest (back stack) pri prebacivanju tabova, što djeluje pokvareno.

ShellRoute to rješava tako da pruža zajednički okvir i ugniježđene navigatore.

ℹ️ Napomena: Flutter navigacija se može ručno složiti s Navigator 2.0, ali go_router standardizira uobičajene obrasce i smanjuje custom boilerplate. Kompromis je da morate poštovati njegov mentalni model: rute predstavljaju stanje.

# go_router postava spremna za produkciju#

Ova postava podržava:

  • deep linkove na mobilnim platformama i webu
  • guardove za autentikaciju i onboarding
  • ShellRoute s donjom navigacijom i neovisnim stackovima
  • stanje vođeno URL-om putem query parametara
  • nastavak nakon logina prema originalno ciljanoj deep poveznici

Definirajte model ruta aplikacije#

Napravite jedan izvor istine za nazive ruta i putanje. Time smanjujete tipfelere i refaktori su sigurniji.

Dart
// app_routes.dart
class AppRoute {
  static const splash = 'splash';
  static const login = 'login';
  static const home = 'home';
  static const search = 'search';
  static const profile = 'profile';
  static const product = 'product';
  static const cart = 'cart';
}
 
class AppPath {
  static const splash = '/';
  static const login = '/login';
  static const home = '/home';
  static const search = '/search';
  static const profile = '/profile';
  static const product = '/p/:id';
  static const cart = '/cart';
}

Osigurajte auth i stanje aplikacije za redirecte#

Redirect treba sinkron pristup stanju. U praksi auth modelirate kao state objekt koji izlaže i trenutni status i Listenable kako bi se redirect ponovno pokretao.

Minimalan pristup je ChangeNotifier wrapper koji zrcali vaš stvarni auth state iz Riverpoda ili Bloca.

Dart
// app_session.dart
import 'package:flutter/foundation.dart';
 
enum AuthStatus { unknown, unauthenticated, authenticated }
 
class AppSession extends ChangeNotifier {
  AuthStatus _status = AuthStatus.unknown;
  bool _onboardingComplete = false;
 
  AuthStatus get status => _status;
  bool get onboardingComplete => _onboardingComplete;
 
  void setAuthenticated({required bool onboardingComplete}) {
    _status = AuthStatus.authenticated;
    _onboardingComplete = onboardingComplete;
    notifyListeners();
  }
 
  void setUnauthenticated() {
    _status = AuthStatus.unauthenticated;
    notifyListeners();
  }
 
  void setUnknown() {
    _status = AuthStatus.unknown;
    notifyListeners();
  }
}

Ako birate state management u 2026., uskladite routing refresh sa svojim pristupom stanju: Flutter state management 2026.

Implementirajte redirect logiku s nastavkom nakon logina#

Najčešći produkcijski zahtjev je nastavak deep linka nakon logina. Možete spremiti pending lokaciju kada preusmjeravate na login.

Neka bude jednostavno: spremite jedan string u session state ili u zaseban koordinator.

Dart
// pending_redirect.dart
class PendingRedirect {
  String? _location;
  String? consume() {
    final value = _location;
    _location = null;
    return value;
  }
 
  void set(String location) {
    _location = location;
  }
}

Sada povežite go_router s:

  • initialLocation za deterministički startup
  • refreshListenable kako bi se redirect ponovno evaluirao kad se auth promijeni
  • redirect koji provodi pravila i sprema pending lokacije
Dart
// app_router.dart
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
 
import 'app_routes.dart';
import 'app_session.dart';
import 'pending_redirect.dart';
 
GoRouter createRouter({
  required AppSession session,
  required PendingRedirect pending,
}) {
  return GoRouter(
    initialLocation: AppPath.splash,
    refreshListenable: session,
    redirect: (context, state) {
      final location = state.uri.toString();
      final isLoggingIn = state.matchedLocation == AppPath.login;
      final isSplash = state.matchedLocation == AppPath.splash;
 
      // Wait for auth bootstrap, token refresh, etc.
      if (session.status == AuthStatus.unknown) {
        return isSplash ? null : AppPath.splash;
      }
 
      // Unauthenticated: send to login, remember intent.
      if (session.status == AuthStatus.unauthenticated) {
        if (!isLoggingIn) {
          pending.set(location);
          return AppPath.login;
        }
        return null;
      }
 
      // Authenticated: prevent going back to login.
      if (session.status == AuthStatus.authenticated && isLoggingIn) {
        final next = pending.consume();
        return next ?? AppPath.home;
      }
 
      // Add onboarding gates here if needed.
      // Example: if onboarding required, redirect to /onboarding.
 
      // Splash should not remain visible when authenticated.
      if (isSplash) return AppPath.home;
 
      return null;
    },
    routes: _routes(session: session),
  );
}

⚠️ Upozorenje: Redirect mora biti brz i bez nuspojava, osim trivialnog in-memory bookkeepinga. Izbjegavajte pozivanje API-ja u redirectu. Ako trebate refresh tokena, odradite to tijekom bootstrapanja aplikacije i držite AuthStatus.unknown dok se ne razriješi.

# ShellRoute za donju navigaciju s neovisnim stackovima#

Standardan obrazac je 3 do 5 tabova, svaki sa svojim ugniježđenim stackom. ShellRoute daje zajednički layout, dok svaka grana zadržava vlastitu povijest.

Primjer stabla ruta#

UX elementStrategija rutaZašto je bitno
Donja navigacijaShellRouteTrajni UI okvir
Stackovi tabovaUgniježđeni Navigator po graniČuva povijest po tabu
Detalji proizvodaTop-level ruta ili ugniježđeno pod tabStabilnost deep linka i ponašanje back gumba
Modalni flowoviparentNavigatorKeyOsigurava ispravan overlay stack

Implementirajte ShellRoute s navigator keyevima#

Tipično imate:

  • root navigator za full-screen rute
  • po jedan navigator za svaku tab granu
Dart
// shell_keys.dart
import 'package:flutter/material.dart';
 
final rootNavigatorKey = GlobalKey<NavigatorState>();
final homeNavigatorKey = GlobalKey<NavigatorState>();
final searchNavigatorKey = GlobalKey<NavigatorState>();
final profileNavigatorKey = GlobalKey<NavigatorState>();

Sada definirajte rute. Shell omata tri glavne grane, a i dalje možete gurati full-screen rute iznad shella.

Dart
// routes.dart
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
 
import 'app_routes.dart';
import 'shell_keys.dart';
 
List<RouteBase> _routes({required dynamic session}) {
  return [
    GoRoute(
      name: AppRoute.splash,
      path: AppPath.splash,
      parentNavigatorKey: rootNavigatorKey,
      builder: (context, state) => const SplashScreen(),
    ),
    GoRoute(
      name: AppRoute.login,
      path: AppPath.login,
      parentNavigatorKey: rootNavigatorKey,
      builder: (context, state) => const LoginScreen(),
    ),
    ShellRoute(
      navigatorKey: rootNavigatorKey,
      builder: (context, state, child) {
        return AppShell(child: child);
      },
      routes: [
        GoRoute(
          name: AppRoute.home,
          path: AppPath.home,
          parentNavigatorKey: homeNavigatorKey,
          builder: (context, state) => const HomeScreen(),
          routes: [
            GoRoute(
              name: AppRoute.product,
              path: 'p/:id',
              builder: (context, state) {
                final id = state.pathParameters['id']!;
                return ProductScreen(productId: id);
              },
            ),
          ],
        ),
        GoRoute(
          name: AppRoute.search,
          path: AppPath.search,
          parentNavigatorKey: searchNavigatorKey,
          builder: (context, state) => const SearchScreen(),
        ),
        GoRoute(
          name: AppRoute.profile,
          path: AppPath.profile,
          parentNavigatorKey: profileNavigatorKey,
          builder: (context, state) => const ProfileScreen(),
        ),
      ],
    ),
    GoRoute(
      name: AppRoute.cart,
      path: AppPath.cart,
      parentNavigatorKey: rootNavigatorKey,
      builder: (context, state) => const CartScreen(),
    ),
  ];
}

Ovo vam daje:

  • /home s ugniježđenim /home/p/123
  • /search i /profile kao odvojene top-level tabove
  • /cart kao full-screen stranicu iznad tabova

💡 Savjet: Cross-cutting full-screen flowove poput checkouta, paywalla i košarice stavite iznad shella koristeći parentNavigatorKey: rootNavigatorKey. Time izbjegavate nezgrapno ponašanje back gumba kad se modal treba zatvoriti natrag na trenutni tab.

# Stanje vođeno URL-om kroz query parametre#

Deep linkovi nisu samo o tome koji se ekran otvara. Radi se i o tome u kojem je stanju taj ekran.

Primjeri koji čine linkove korisnima:

  • /search?q=ipad&sort=price_desc
  • /home?tab=offers
  • /profile?section=billing

Sigurno čitanje query parametara#

Query parametre tretirajte kao nepouzdan input. Validirajte ih i postavite zadane vrijednosti.

Dart
// search_screen.dart (snippet)
class SearchArgs {
  final String query;
  final String sort;
 
  SearchArgs({required this.query, required this.sort});
 
  factory SearchArgs.fromUri(Uri uri) {
    final q = uri.queryParameters['q'] ?? '';
    final sort = uri.queryParameters['sort'] ?? 'relevance';
    return SearchArgs(query: q, sort: sort);
  }
}

U route builderu:

Dart
builder: (context, state) {
  final args = SearchArgs.fromUri(state.uri);
  return SearchScreen(initialQuery: args.query, initialSort: args.sort);
},

Ažuriranje URL-a kad se stanje promijeni#

Kad korisnik promijeni filtere, ažurirajte lokaciju rute. To čini back gumb smislenim i omogućuje dijeljive URL-ove na webu.

Dart
// in SearchScreen
void updateSearch(BuildContext context, {required String q, required String sort}) {
  final uri = Uri(path: AppPath.search, queryParameters: {
    'q': q,
    'sort': sort,
  });
  context.go(uri.toString());
}

🎯 Ključna poruka: Ako je stanje korisniku važno, obično treba živjeti u URL-u. Time smanjujete skriveno stanje, poboljšavate ponašanje back gumba i činite deep linkove stabilnima na mobilnim platformama i webu.

# Deep linkovi na iOS-u, Androidu i webu#

go_router rješava routing nakon što aplikacija primi link. Postavke platforme i dalje su važne.

Odgovornosti platforme naspram odgovornosti go_routera#

SlojOdgovornostTipične zamke
iOS universal linksAssociated domains, apple-app-site-associationPogrešan content-type, cacheiranje, nedostajuće putanje
Android app linksintent filteri, assetlinks.json, SHA-256 fingerprintiDebug keystore nije dodan, više build varijanti
Flutter go_routerParsiranje lokacije i render stabla rutaRedirect petlje, nedostajuća validacija parametara
WebURL strategija i server rewrites404 na refresh bez rewrite pravila

Za kompletne detalje postavki platforme, koristite end-to-end vodič: Flutter deep linking: universal links, app links, routing.

Podrška za web: server rewrites nisu opcionalni#

Na Flutter webu korisnici mogu osvježiti /home/p/123. Ako hosting ne radi rewrite nepoznatih putanja na index.html, dobit ćete 404 iako aplikacija može obraditi rutu.

Uobičajena rewrite pravila:

  • Firebase Hosting: rewrite svega na /index.html
  • Vercel: rewrite na /index.html za SPA output
  • Nginx: try_files $uri $uri/ /index.html;

Također provjerite ne miješate li hash i path URL strategije nenamjerno. Path-based URL-ovi su bolji za dijeljivost nalik SEO-u, ali zahtijevaju rewrites.

# Testiranje deep linkova: cold start, warm start i rubni slučajevi#

Deep linkovi u produkciji najčešće padaju zbog netestiranih lifecycle uvjeta. Trebate testirati:

  • cold start bez sesije
  • cold start s isteklom sesijom
  • warm start dok je aplikacija u pozadini
  • aplikacija već otvorena na drugom tabu
  • link s nedostajućim ili nevažećim parametrima
  • linkove koji trebaju otvoriti modalni flow iznad shella

Praktična matrica testova#

ScenarijOčekivano ponašanjeŠto se često pokvari
Cold start, odjavljen, otvori /home/p/123Preusmjeri na login, zatim nastavi na proizvodPending lokacija se izgubi
Warm start, prijavljen, otvori /search?q=xNavigira na search s queryjemStaro stanje ostane
Nevažeći id /home/p/abc kad id mora biti numeričkiPrikaži not found ili greškuCrash zbog parsiranja
Web refresh na /profileObnovi se ista rutaServer vrati 404
Deep link na /cart iz notifikacijeOtvori košaricu iznad shellaZavršava unutar tab stacka

Naredbe za okidanje deep linkova#

Android:

Bash
adb shell am start -a android.intent.action.VIEW \
  -d "https://example.com/home/p/123" \
  com.example.app

iOS Simulator:

Bash
xcrun simctl openurl booted "https://example.com/home/p/123"

Flutter web lokalno:

Bash
flutter run -d chrome --web-port 5173
# then open http://localhost:5173/home/p/123

Redirect petlje i kako ih spriječiti#

Redirect petlje obično se događaju kada:

  • login ruta također okida redirect natrag na login
  • splash nastavlja preusmjeravati dok je auth unknown
  • obavezni parametar nedostaje, ali redirect šalje na rutu koja također ne prolazi

Praktičan obrazac je:

  • dopustiti /login kad je korisnik unauthenticated
  • dopustiti / splash samo kad je status unknown
  • validirati parametre ruta i vratiti se na zasebnu not-found rutu

Dodajte error ekran:

Dart
GoRouter(
  errorBuilder: (context, state) => NotFoundScreen(message: state.error.toString()),
  // ...
);

I validirajte parametre:

Dart
final raw = state.pathParameters['id'];
if (raw == null) return const NotFoundScreen(message: 'Missing product id');

⚠️ Upozorenje: Nikad ne pretpostavljajte da su query parametri prisutni ili valjani u deep linkovima. Sve u state.uri tretirajte kao korisnički input koji može biti krivo formatiran, skraćen ili namjerno zlonamjeran.

# Kako routing utječe na arhitekturu aplikacije#

Odluke o routingu određuju granice između značajki. Ako su rute razbacane po UI widgetima, mučit ćete se s:

  • dodavanjem novih guardanih flowova
  • dosljednom implementacijom analitike
  • podrškom za web URL-ove i dijeljive linkove
  • skaliranjem timova po značajkama bez merge konflikata

Preporučena struktura za skalabilan router#

Pragmatičan feature-first pristup:

FolderSadržiZašto
lib/core/routingkreiranje routera, redirect pravila, imena rutaCentralna politika
lib/features/homehome ekrani, ugniježđene ruteVlasništvo featurea
lib/features/authlogin flow, session stateAuth granica
lib/features/searchfilteri vođeni URL-omDijeljivo stanje
lib/core/analyticsnavigation observeriDosljednost

Ako želite puni breakdown feature-first pristupa i čistih granica: Flutter app architecture: clean architecture, feature-first.

U produkciji navigaciju promatrajte kao state machine pokretan:

  • auth stanjem
  • onboarding stanjem
  • subscription stanjem
  • stanjem URL lokacije

Redirect funkcija je logika prijelaza. Držite je determinističnom i testabilnom tako da je izvučete u čistu (pure) funkciju.

Primjer ideje za signature:

  • ulaz: session state, trenutna lokacija
  • izlaz: sljedeća lokacija ili null

To olakšava unit testiranje bez widget testova.

Analitika i privatnost#

Deep linkovi i query parametri često sadrže osjetljive podatke. Izbjegavajte stavljati e-mailove, tokene ili PII u URL-ove jer:

  • URL-ovi se logiraju u analitičkim alatima
  • URL-ove mogu uhvatiti proxyji i server logovi na webu
  • korisnici dijele URL-ove

Umjesto ?email=a@b.com, koristite neprozirni id ili one-time token s kratkim TTL-om i validirajte na serveru.

# Ključne poruke#

  • Modelirajte ekrane koji se mogu deep linkati kao determinističke URL-ove, uključujući smisleno stanje kroz query parametre.
  • Koristite refreshListenable i brzu redirect funkciju za auth guardove i onboarding pravila bez API poziva unutar redirecta.
  • Koristite ShellRoute za donju navigaciju s neovisnim stackovima, a modalne ili checkout flowove gurajte iznad shella preko root navigatora.
  • Implementirajte nastavak nakon logina spremanjem pending lokacije, pa njenim „konzumiranjem” nakon uspješne autentikacije.
  • Testirajte deep linkove kroz cold start, warm start, nevažeće parametre i web refresh uz ispravna server rewrite pravila.

# Zaključak#

Robusna postava Flutter go_router deep links je razlika između navigacijskog demo projekta i produkcijske aplikacije koja preživi stvarne korisnike, stvarne URL-ove i stvarne rubne slučajeve. Ako implementirate determinističke URL-ove, redirecte vođene politikama, ugniježđenu navigaciju preko ShellRoutea i stanje vođeno URL-om, dobivate dijeljive linkove, ispravno back ponašanje i routing sloj koji se skalira s vašom arhitekturom.

Ako želite da vam Samioda pomogne dizajnirati produkcijsku strategiju navigacije i deep linkinga, uključujući konfiguraciju platforme, redirect politike i feature-first strukturu routinga, kontaktirajte nas i pregledat ćemo vaš trenutni router te predložiti „otvrdnuti” plan implementacije.

FAQ

Share
A
Adrijan OmićevićOsnivač i senior developer

Osnivač i senior developer u Samiodi. 8+ godina iskustva u izradi React, Next.js, Flutter i n8n rješenja za klijente diljem Europe.

Više iz kategorije Mobilni razvoj

Sve
·15 min čitanja

Ojačavanje sigurnosti Flutter aplikacije: SSL pinning, detekcija roota i jailbreaka te sigurna pohrana (Vodič za 2026.)

Praktičan vodič za ojačavanje sigurnosti Flutter aplikacije uz checklistu temeljenu na threat modelu, obrasce sigurne pohrane, kompromise SSL pinninga, provjere integriteta u runtimeu i sigurno rukovanje auth tokenima.

FlutterSigurnost mobilnih aplikacijaSSL PinningSigurna pohranaOWASP MASVSAutentikacija
Adrijan OmićevićPročitaj članak
·13 min čitanja

Flutter zadaci u pozadini: raspoređivanje, pouzdanost i ograničenja platformi (iOS + Android) u 2026.

Praktičan vodič za raspoređivanje Flutter zadataka u pozadini: ograničenja platformi na iOS-u i Androidu, kompromisi pouzdanosti te kada koristiti workmanager, background_fetch ili izvorni kod za sinkronizaciju, obavijesti i raspoređivanje koje čuva bateriju.

FlutterRazvoj mobilnih aplikacijaAndroidiOSZadaci u pozadiniRaspoređivanjeSinkronizacija
Adrijan OmićevićPročitaj članak
·14 min čitanja

Flutter dizajnerski sustav i tematizacija u 2026.: Material 3, dinamičke boje, tipografija i tamni način rada

Praktičan vodič za tematizaciju Flutter dizajnerskog sustava uz Material 3: skalabilna arhitektura teme, dizajnerski tokeni, dinamičke boje, tipografija, razmaci, tematizacija komponenti i konzistentan tamni način rada kroz sve funkcionalnosti.

FlutterMaterial 3Dizajnerski sustavTematizacijaRazvoj mobilnih aplikacijaTamni način radaTipografija
Adrijan OmićevićPročitaj članak

Trebate pomoć s projektom?

Gradimo prilagođena rješenja koristeći tehnologije iz ovog članka. Senior tim, fiksne cijene.

Povezani članci

·13 min čitanja

Vodič za Flutter deep linking za 2026.: Universal Links, Android App Links i pouzdano rutiranje unutar aplikacije

Praktičan, produkcijski spreman vodič za Flutter deep linking: Universal Links, Android App Links, go_router obrada ruta, deferred deep linkovi, osnove atribucije u analitici i kontrolna lista za rješavanje problema.

FlutterRazvoj mobilnih aplikacijaDeep linkingUniversal LinksAndroid App Linksgo_routerAnalitika
Adrijan OmićevićPročitaj članak
·13 min čitanja

Flutter zadaci u pozadini: raspoređivanje, pouzdanost i ograničenja platformi (iOS + Android) u 2026.

Praktičan vodič za raspoređivanje Flutter zadataka u pozadini: ograničenja platformi na iOS-u i Androidu, kompromisi pouzdanosti te kada koristiti workmanager, background_fetch ili izvorni kod za sinkronizaciju, obavijesti i raspoređivanje koje čuva bateriju.

FlutterRazvoj mobilnih aplikacijaAndroidiOSZadaci u pozadiniRaspoređivanjeSinkronizacija
Adrijan OmićevićPročitaj članak
·14 min čitanja

Flutter dizajnerski sustav i tematizacija u 2026.: Material 3, dinamičke boje, tipografija i tamni način rada

Praktičan vodič za tematizaciju Flutter dizajnerskog sustava uz Material 3: skalabilna arhitektura teme, dizajnerski tokeni, dinamičke boje, tipografija, razmaci, tematizacija komponenti i konzistentan tamni način rada kroz sve funkcionalnosti.

FlutterMaterial 3Dizajnerski sustavTematizacijaRazvoj mobilnih aplikacijaTamni način radaTipografija
Adrijan OmićevićPročitaj članak