GoRouter is a routing library for Flutter that simplifies navigation, URL handling, and deep linking. It is the officially recommended solution by the Flutter team for building modern, declarative navigation.Instead of manually pushing screens, you describe your app's routes as paths (for example /login or /details/:id).
Navigation quickly becomes complex in real apps.
GoRouter helps by:
It is especially useful for apps that need authentication flows or web support.
It acts as a wrapper around Flutter's Navigator 2.0 (Router API), maintaining a synchronized state between the operating system’s intent (like a browser URL) and the app's navigation stack. When a route changes, instead of simply pushing a new screen, the engine diffs the new configuration against the current stack and performs the minimal necessary push/pop operations to match the target state.
GoRouter maps URLs to widgets. When a navigation event happens, it:
This approach works the same on Android, iOS, and Flutter Web.
context.go('/details/42');This navigates to a route like /details/:id and rebuilds the stack to match the URL.
context.go: changes the current location and may reset navigation history.context.push: adds a new screen on top and keeps the back button.Use go for changing app sections, and push for temporary or detail screens.
GoRouter handles deep link logic inside the app, but system configuration is still required:
AndroidManifest.xmlInfo.plistWithout this, links will not open your app.
context.go like Navigator.push.GoRouter is particularly useful in Flutter applications that require structured and predictable navigation. It shines when your app has deep links, needs to support web URLs, or must handle authentication flows and conditional redirects. For instance, in an e-commerce app, you might want marketing links to open a specific product page directly, or redirect users to a login page if they are not authenticated.
Additionally, GoRouter provides a declarative, URL-driven approach to navigation that simplifies maintaining and testing complex navigation trees. Apps with nested routes, tab-based layouts, or multiple navigators benefit from its consistent syntax and integration with Flutter's widget tree. Using GoRouter allows developers to centralize navigation logic, making it easier to track route changes, handle route guards, and manage state restoration across sessions or devices.
GoRouter may be unnecessary for very small applications with straightforward navigation flows, such as apps with only a handful of screens or simple back-and-forth navigation. In these cases, Flutter’s built-in Navigator 1.0 API is often sufficient and avoids the extra dependency and learning curve.
It is also less useful in projects that have already been fully implemented using Navigator 1.0, especially if the app is stable and the navigation structure is unlikely to change. Introducing GoRouter in such projects could add unneeded complexity without clear architectural benefits.
For simple, linear navigation or apps where URL-based routing is not required, sticking to the standard Navigator can keep the widget tree simpler and reduce maintenance overhead.
12 min • Jul 27, 2023
Read about the LeanCode approach to Flutter architecture. We highlight some of the design decisions that should be made when developing a feature in mobile apps. This includes our approach to dependency injection, state management, widget lifecycle, and data fetching.
5 min • Jan 13, 2022
Matej Rešetár, during Flutter Warsaw #16, talked about using Flutter for the web and explained to avoid the most common issues. Find out more about making mobile apps run on the web with Flutter by watching the video and reading participants' questions.
10 min • Oct 27, 2025
In this article, we’re sharing LeanCode’s 12 practical Flutter and Dart patterns that help you write less boilerplate, make your code cleaner, and catch mistakes earlier. Apply these patterns and you'll find yourself coding faster, communicating more clearly with your teammates, and spending less time debugging issues that the compiler could have caught.