Jun 16, 2025
Advanced Navigation in Flutter Web: A Deep Dive with Go Router
Master advanced navigation in Flutter Web using go router. Learn deep linking, auth redirects, ShellRoute layouts, and more to scale your app like a pro.
Author

Subject Matter Expert


Book a call
Table of Contents
- Declarative route definitions,
- Built-in support for redirection (ideal for authentication),
- Seamless deep linking,
- URL synchronization,
- and platform-agnostic design (supporting mobile, web, and desktop).
In this blog, we go beyond the basics — diving into how go_router can be harnessed to build sophisticated navigation setups with app bars, bottom nav bars, deep links, and more — all while keeping your code manageable and your routes meaningful.
In this version, we use a single skeleton class (the shell) that holds the shared AppBar and BottomNavigationBar. All the sub-screens are rendered inside this skeleton layout as children of the ShellRoute.
- Each time you switch screens, Flutter rebuilds the entire page, including the AppBar and BottomNavigationBar.
- Your layout flashes or resets unnecessarily.
- You duplicate UI code (same Scaffold, same AppBar) across every screen.
- On the web, this feels clunky — like the whole page is reloading.
- And if you try to restore a tab via URL (e.g., going directly to /search), your layout structure is gone.
That’s when you realize: you need a shared layout — one that wraps all your routes and stays constant, while only the inner content updates.
If you look at the go_router code using ShellRoute, it follows this structure:
- A parent shell defines the shared layout (AppBar, BottomNavigationBar).
- Inside this ShellRoute, we define the child routes.
- When a user taps a tab, only the child content changes — the shared layout remains intact.
- You preserve state,
- You reduce redundant code,
You get smooth, user-friendly navigation — just like a native mobile app.
Managing Authentication flows using redirect methods.
- Redirect users if their token is missing or expired
- Prevent access to protected pages when not logged in
- Automatically send logged-in users to the home screen if they open the login screen
- Global Redirect (at the GoRouter level):
Best for app-wide decisions like login state. - Per-route Redirect (at the GoRoute level):
Useful for more granular rules, like checking role-specific access or route-specific preloading.
Now let us look at some code:
Now that we've used a global redirect, let’s talk about per-route redirects in GoRoute.
- Global redirect in the GoRouter class
- Per-route redirect in the individual GoRoute
- builder method of the intended route
Supporting Intended URLs (Preserving the Target Route Before Login)
- They are redirected to the login screen.
- But after a successful login, they’re automatically taken back to /product/123.
- The redirect method (global or per-route)
- A shared state (e.g., using Provider, Riverpod, or any other state management solution) to store the intended URL
- In the global redirect, check if the user is unauthenticated.
- If they are, store state.uri.toString() (which holds the full requested path).
- Redirect them to the login page.
- After successful login, check if there was a stored path.
- Navigate back to the stored URL
In the global redirect:
- A ChangeNotifier, StateNotifier, or Riverpod provider
- Or even a simple singleton service
- Protected routes
- Deep linking
- Session-expired flows
- Checkout or payment pages
It provides a professional UX where the app never forgets where the user was going.
Taking Advantage of URLs in Flutter Web with go_router
- Path parameters to encode resource identifiers
- Query parameters to manage filters, search, and UI state
- Clean and structured routes that make the app easier to understand and debug
Path Parameters
Example URL:
Example route:
This pattern makes URLs more readable and allows direct navigation to specific resources.
Query Parameters
Example URL:
Example route:
This makes your app’s state reflective in the URL, which improves shareability and supports refresh and bookmarking.
Syncing UI State with URLs
- The selected tab: /dashboard?tab=analytics
- The current page: /products?page=2
- The active filter: /products?category=electronics&inStock=true
- Restoring state on refresh
- Browser back/forward button functionality
Link sharing with complete context
Passing Complex Data Beyond Simple Strings in go_router
- Full model objects
- Maps or JSON-like structures
- UI-related state (like a ScrollController)
- Navigation context (e.g., came from search page)
1. Passing a Custom Class Model Let’s say you have a ProductModel class and want to send the whole object to a detail screen:
Useful when passing form data or filters.
For example, cart items:
Say you have a sorting enum:
Rare, but possible if you're controlling some behavior:
- state.extra is not persisted — if the user refreshes or shares the URL, the data is lost.
- Don’t use it for critical state that must survive a browser reload.
Combine it with state.pathParameters or query parameters if you need both persisted and transient data.
Identifying and Resolving Potential Issues with go_router
1. StatefulShellBranch does not support parameterized default locations
2. Popping nested navigation affects parent stack unexpectedly
Example:
Wrap the nested screen with RouteNeglect like this:
3. state.extra is lost on browser refresh
- Store critical values in the URL or use local storage/state management to persist.
- Use queryParameters or embed the data in pathParameters if small enough.
4. redirect doesn't wait for async operations (e.g. fetching from shared preferences)
- Use a loading screen while the app is initializing.
5. Default behavior of .go() wipes navigation stack
- Use .push() or .pushReplacement() as needed.
- Understand that .go() resets the stack, which is useful for deep links but not always desirable during internal navigation.
Navigating multi-screen Flutter web apps with go_router goes far beyond just pushing and popping routes. From managing authentication flows and preserving intended URLs, to leveraging state.extra for complex data and identifying subtle bugs through real examples — go_router gives you the flexibility and control you need. Whether you're building a large-scale app or a focused product experience, mastering go_router will help you build intuitive, robust navigation flows on the web.
Subscribe to Our Newsletter
Subscribe to RSS
Press & Media Hub RSS FeedRelated Articles.
More from the engineering frontline.
Dive deep into our research and insights on design, development, and the impact of various trends to businesses.

Nov 19, 2025
Offline-First Flutter: Implementation Blueprint for Real-World Apps

Nov 27, 2024
Advanced Network Architecture for a Scalable ERP System

Sep 26, 2024
How to Implement Branch Deep Linking in Flutter

Sep 25, 2024
Converting Flutter Screens to Shareable PDFs: A Complete Guide

Sep 12, 2024
Boosting SEO in Flutter Web Apps: A Guide for Limited Pages

Sep 6, 2024