GoRouter is a popular package for declarative routing in Flutter. It based on the Navigator 2.0 API, and supports deep linking and other common navigation scenarios, all behind an easy to use API.
If you're coming from Navigator 1.0, you'll be familiar with the concept of pushing a route to the navigation stack.
But when using GoRouter, you have two separate options:
- going to a route
- pushing a route
This article will explore the difference between the two, so that you can choose the most appropriate one on a case-by-case basis.
sponsor

Build and grow in-app purchases. Glassfy’s Flutter SDK solves all the complexities and edge cases of in-app purchases and subscriptions so you don't have to. Test and build for free today by clicking here.
Declarative routing with GoRouter
To start off, let's consider a simple route hierarchy made of one top route with two sub-routes:
GoRouter(
initialLocation: '/',
routes: [
// top-level route
GoRoute(
path: '/',
builder: (context, state) => const HomeScreen(),
routes: [
// one sub-route
GoRoute(
path: 'detail',
builder: (context, state) => const DetailScreen(),
),
// another sub-route
GoRoute(
path: 'modal',
pageBuilder: (context, state) => const MaterialPage(
fullscreenDialog: true,
child: ModalScreen(),
),
)
],
),
],
)
Let's also define 3 pages for our routes:
Navigating from the top route
Now, suppose that we're in the HomeScreen
, which is just a simple page with three buttons, with callbacks defined like this:
// onPressed callback for the first button
context.go('/detail'),
// onPressed callback for the second button
context.push('/detail'),
// onPressed callback for the third button
context.go('/modal'),
The first and second callback have the same target location (/detail
), and as a result they behave in the same way.
That is, in both cases we'll end up with two routes in the navigation stack (home → detail).
The difference between Go and Push
From the detail page, we can now navigate to /modal
in two different ways:
// onPressed callback for the first button
context.go('/modal'),
// onPressed callback for the second button
context.push('/modal'),
This time the result is different:
- If we use
go
, we end up with the modal page on top of the home page - If we use
push
, we end up with the modal page on top of the detail page
That's because go
jumps to the target route (/modal
) by discarding the previous route (/detail
), since /modal
is not a sub-route of /detail
:
Meanwhile, push
always adds the target route on top of the existing one, preserving the navigation stack.
This means that once we dismiss the modal page, we navigate back to:
- the home page, if we used
go
- the detail page, if we used
push
Here's a short video showing this behavior:
And here's a gist with the full source code:
Conclusion
The bottom line?
Think of go
as a way to jump to a new route. This will modify the underlying navigation stack if the new route is not a sub-route of the old one.
On the other hand, push
will always push the destination route on top of the existing navigation stack.
For more info about GoRouter, you can check the API docs or head over to the old documentation site (which is a bit outdated but still an excellent resource).
New Flutter Course Now Available
I launched a brand new course that covers GoRouter in great depth, along with other important topics like state management, app architecture, testing, and much more: