What is the difference between go and push in go-router?
#route#go_router#navigation#routing
Answer
Overview
In Flutter's
text
go_routertext
go()text
push()Key Differences
| Feature | text | text |
|---|---|---|
| Stack Behavior | Replaces stack | Adds to stack |
| Navigation Type | Declarative | Imperative |
| Equivalent to | text | text |
| Use Case | Navigate and clear history | Navigate and keep history |
| Back Button | May not return to previous screen | Returns to previous screen |
| URL Sync | Updates browser URL (web) | Adds to browser history |
context.go() - Replace Stack
text
context.go()When to Use go()
- After successful login (navigate to home)
- Completing onboarding flow
- Navigating between main sections
- When you don't want users to go back
Example
dart// After successful login ElevatedButton( onPressed: () { // User cannot go back to login screen context.go('/home'); }, child: Text('Login'), )
context.push() - Add to Stack
text
context.push()When to Use push()
- Opening detail screens
- Showing modal pages
- Navigating to forms or settings
- When users should return to previous screen
Example
dart// Open product details ListTile( onTap: () { // User can go back to product list context.push('/product/${product.id}'); }, title: Text(product.name), )
Practical Comparison
Scenario 1: Simple Navigation
dart// Current stack: ['/', '/home'] // Using go('/details') context.go('/details'); // Result: ['/', '/details'] - /home is removed // Using push('/details') context.push('/details'); // Result: ['/', '/home', '/details'] - /home is kept
Scenario 2: Sub-routes
dart// Current stack: ['/', '/home'] // Using go('/home/settings') - settings is sub-route of home context.go('/home/settings'); // Result: ['/', '/home', '/home/settings'] - home is kept // Using push('/modal') - modal is NOT sub-route of home context.push('/modal'); // Result: ['/', '/home', '/modal'] - adds to stack // Using go('/modal') - modal is NOT sub-route of home context.go('/modal'); // Result: ['/', '/modal'] - home is removed
Complete Example
dartimport 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; // Router configuration final GoRouter router = GoRouter( routes: [ GoRoute( path: '/', builder: (context, state) => HomeScreen(), ), GoRoute( path: '/login', builder: (context, state) => LoginScreen(), ), GoRoute( path: '/details/:id', builder: (context, state) { final id = state.pathParameters['id']!; return DetailsScreen(id: id); }, ), ], ); class HomeScreen extends StatelessWidget { Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Home')), body: Column( children: [ ElevatedButton( onPressed: () { // Use push - can go back to home context.push('/details/123'); }, child: Text('View Details (Push)'), ), ElevatedButton( onPressed: () { // Use go - login replaces home in stack context.go('/login'); }, child: Text('Logout (Go)'), ), ], ), ); } } class LoginScreen extends StatelessWidget { Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Login')), body: ElevatedButton( onPressed: () { // Use go - user cannot go back to login context.go('/'); }, child: Text('Login'), ), ); } }
Return Values
Both methods can return values, but handle it differently:
dart// Using push - can await result final result = await context.push('/form'); if (result == true) { print('Form submitted'); } // Using go - cannot await result directly context.go('/form');
Best Practices
-
Use
for:textgo()- Authentication flows (login → home)
- Main navigation tabs/sections
- Completing multi-step processes
- When back navigation doesn't make sense
-
Use
for:textpush()- Detail screens
- Forms and dialogs
- Settings and preferences
- Any screen where users should return
-
Avoid mixing both methods unnecessarily as it can confuse navigation behavior
-
Test navigation on both mobile and web to ensure expected behavior