DEV Community

Twilight
Twilight

Posted on

Understanding `context.push` vs `context.go` in Flutter's GoRouter

Navigating between screens is a fundamental aspect of any Flutter application. With the advent of the GoRouter package, managing routes has become more streamlined and declarative. However, understanding the nuances between different navigation methods is crucial for maintaining a predictable navigation stack. In this article, we'll delve into the differences between context.push and context.go in GoRouter, focusing on their behaviors in various routing scenarios with concrete examples.


πŸ“š Table of Contents

  1. Introduction to GoRouter
  2. context.push vs context.go
  3. Practical Examples
  4. Conclusion

πŸš€ Introduction to GoRouter

GoRouter is a powerful routing package for Flutter that simplifies navigation and deep linking. It leverages Flutter's Navigator 2.0 API, providing a declarative approach to defining routes and handling navigation actions. Two primary methods for navigation in GoRouter are context.push and context.go. Understanding their differences is essential for effective route management.


context.push vs context.go

Both context.push and context.go are used to navigate between routes, but they behave differently depending on the routing structure and the desired navigation behavior.

context.push

  • Purpose: Adds a new route on top of the current navigation stack.
  • Equivalent To: Navigator.push in Flutter's native Navigator.
  • Use Case: When you want to navigate to a new screen without removing the current one, allowing the user to return to the previous screen.

context.go

  • Purpose: Replaces the current stack of screens with the screens configured for the destination route.
  • Equivalent To: Navigator.pushNamedAndRemoveUntil with a predicate that removes all existing routes (e.g., (Route<dynamic> route) => false).
  • Use Case: When you want to navigate to a new screen and remove all previous routes, such as navigating to a home screen after a successful login or redirecting to a splash screen.

πŸ“Š Key Differences

Aspect context.push context.go
Stack Behavior Adds to the existing stack Replaces the current stack with the destination route stack
Navigation Depth Maintains history for back navigation Clears history unless navigating to a nested route, which preserves parent routes
Use Cases Navigating to detail screens, modals Redirecting after authentication, resetting navigation, navigating to a new root screen
Equivalent Methods Navigator.push Navigator.pushNamedAndRemoveUntil with a predicate to remove all previous routes

πŸ‘―β€β™‚οΈ Behavior with Sibling Routes

Scenario: You have two sibling routes, Login and Profile, both defined at the same hierarchical level.

Using context.push

  • Initial Stack: [Login]
  • Action: context.push('/profile')
  • Resulting Stack: [Login, Profile]
  • Behavior: Profile is added on top of Login, allowing the user to navigate back to Login.

Using context.go

  • Initial Stack: [Login]
  • Action: context.go('/profile')
  • Resulting Stack: [Profile]
  • Behavior: The entire stack is replaced with Profile, removing Login from the history.

🏠 Behavior with Nested Routes

Scenario: Route Settings is a subroute (child) of route Home.

Using context.push

  • Initial Stack: [Home]
  • Action: context.push('/home/settings')
  • Resulting Stack: [Home, Settings]
  • Behavior: Settings is added as a child of Home, maintaining the parent-child relationship. The user can navigate back to Home.

Using context.go

  • Initial Stack: [Home]
  • Action: context.go('/home/settings')
  • Resulting Stack: [Home, Settings]
  • Behavior: Since Settings is a subroute of Home, Home remains in the stack, and Settings is added on top. The overall stack structure preserves the parent-child relationship, allowing back navigation to Home.

πŸ” Practical Examples

Let's solidify our understanding with practical code examples using concrete route names like /login, /home, /home/settings, and /profile.

πŸ“‚ Route Configuration

import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

final GoRouter router = GoRouter(
  initialLocation: '/login',
  routes: [
    GoRoute(
      path: '/login',
      name: 'Login',
      builder: (context, state) => const LoginScreen(),
    ),
    GoRoute(
      path: '/home',
      name: 'Home',
      builder: (context, state) => const HomeScreen(),
      routes: [
        GoRoute(
          path: 'settings',
          name: 'Settings',
          builder: (context, state) => const SettingsScreen(),
        ),
      ],
    ),
    GoRoute(
      path: '/profile',
      name: 'Profile',
      builder: (context, state) => const ProfileScreen(),
    ),
  ],
);
Enter fullscreen mode Exit fullscreen mode

πŸ› οΈ LoginScreen with Navigation Buttons

class LoginScreen extends StatelessWidget {
  const LoginScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    // Simulate a successful login
    return Scaffold(
      appBar: AppBar(title: const Text('Login')),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            // Navigate to Home after login
            context.go('/home');
          },
          child: const Text('Login and Go to Home'),
        ),
      ),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

🏠 HomeScreen with Navigation Buttons

class HomeScreen extends StatelessWidget {
  const HomeScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    // Buttons to navigate to Settings and Profile
    return Scaffold(
      appBar: AppBar(title: const Text('Home')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            ElevatedButton(
              onPressed: () => context.push('/home/settings'),
              child: const Text('Push to Settings (Nested)'),
            ),
            ElevatedButton(
              onPressed: () => context.go('/profile'),
              child: const Text('Go to Profile (Sibling)'),
            ),
          ],
        ),
      ),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

βš™οΈ SettingsScreen with Back Navigation

class SettingsScreen extends StatelessWidget {
  const SettingsScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    // Button to navigate back to Home
    return Scaffold(
      appBar: AppBar(title: const Text('Settings')),
      body: Center(
        child: ElevatedButton(
          onPressed: () => context.pop(),
          child: const Text('Go Back to Home'),
        ),
      ),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

πŸ‘€ ProfileScreen with Back Navigation

class ProfileScreen extends StatelessWidget {
  const ProfileScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    // Button to navigate back to Home (if possible)
    return Scaffold(
      appBar: AppBar(title: const Text('Profile')),
      body: Center(
        child: ElevatedButton(
          onPressed: () => context.pop(),
          child: const Text('Go Back'),
        ),
      ),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

πŸ”Ž Expected Behaviors

  1. Login and Go to Home

    • Method: context.go('/home')
    • Stack Transition: [Login] β†’ [Home]
    • Back Navigation: 🚫 Disabled (cannot go back to Login)
  2. Push to Settings (/home/settings)

    • Method: context.push('/home/settings')
    • Stack Transition: [Home] β†’ [Home, Settings]
    • Back Navigation: πŸ”™ Enabled (can go back to Home)
  3. Go to Profile (/profile)

    • Method: context.go('/profile')
    • Stack Transition: [Home] β†’ [Profile]
    • Back Navigation: 🚫 Disabled (cannot go back to Home)
  4. Go Back from Settings

    • Method: context.pop()
    • Stack Transition: [Home, Settings] β†’ [Home]
    • Back Navigation: πŸ”™ Enabled (can go back to Home)

βœ… Conclusion

Understanding the distinction between context.push and context.go in GoRouter is essential for effective navigation management in Flutter applications:

  • Use context.push when you want to add a new route on top of the current stack, preserving the ability to navigate back. This is ideal for scenarios like navigating to detail screens or opening modals where the previous screen remains relevant.

  • Use context.go when you intend to replace the current navigation stack with a new route. This is particularly useful for actions like redirecting users after authentication, resetting navigation history, or navigating to a new root screen where returning to previous screens is not desired.


Happy Coding! πŸš€

Top comments (0)