Popular Searches
Popular Course Categories
Popular Courses

Navigator Push and Pop

Flutter Navigation & Screens


Navigator Push and Pop in Flutter


Flutter applications commonly contain multiple screens, such as a Home screen, Login screen, Product Details screen, Profile screen, Settings screen, and Checkout screen. Flutter uses the Navigator to move between these screens. In Flutter terminology, screens and pages are called routes.


The two fundamental Navigator operations are Navigator.push(), which opens a new screen, and Navigator.pop(), which closes the current screen and returns to the previous screen.




1. What is Navigator in Flutter?


The Navigator is a Flutter widget that manages a stack of routes. Each time a new screen is opened, a route is added to the stack. When the current screen is closed, that route is removed from the stack.


For example, suppose an application has these screens:



  1. Home Screen

  2. Product Screen

  3. Product Details Screen


The navigation stack can be visualized as:


Home Screen
    ↓
Product Screen
    ↓
Product Details Screen

When the user presses the back button from Product Details, the current route is removed and the user returns to Product Screen.




2. Understanding the Navigator Stack


Flutter's Navigator maintains a stack of routes. A stack follows the LIFO (Last In, First Out) principle.








OperationEffect
Initial screenHome route is placed on the stack
push()Adds a new route to the top
Another push()Adds another route to the top
pop()Removes the top route

Example:


Navigator Stack

Before push:
[ Home ]

After push(ProductScreen):
[ Home ]
[ ProductScreen ]

After push(DetailsScreen):
[ Home ]
[ ProductScreen ]
[ DetailsScreen ]

After pop():
[ Home ]
[ ProductScreen ]




3. What is Navigator.push()?


Navigator.push() is used to navigate from the current screen to a new screen. It adds a new route to the Navigator's route stack.


Flutter's official navigation documentation demonstrates using Navigator.push() together with MaterialPageRoute to open another screen.


Basic Syntax


Navigator.push(
  context,
  MaterialPageRoute(
    builder: (context) => const SecondScreen(),
  ),
);

How it works



  1. The user performs an action, such as tapping a button.

  2. Navigator.push() is called.

  3. A new Route is created.

  4. The new route is added to the Navigator stack.

  5. The new screen becomes visible.




4. What is MaterialPageRoute?


MaterialPageRoute creates a route using Material Design navigation behavior and provides a platform-appropriate transition animation.


Example:


Navigator.push(
  context,
  MaterialPageRoute(
    builder: (context) => const DetailsScreen(),
  ),
);

The builder function returns the widget that should be displayed on the new route.




5. What is Navigator.pop()?


Navigator.pop() is used to remove the current route from the Navigator stack and return to the previous route.


Basic Syntax


Navigator.pop(context);

For example, if the current screen is DetailsScreen and the previous screen is HomeScreen, calling Navigator.pop(context) closes DetailsScreen and displays HomeScreen.




6. Basic Navigator Push and Pop Example


The following example creates two screens. The first screen uses Navigator.push() to open the second screen, while the second screen uses Navigator.pop() to return to the first screen.


import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Navigator Example',
      home: const HomeScreen(),
    );
  }
}

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Home Screen'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) => const SecondScreen(),
              ),
            );
          },
          child: const Text('Open Second Screen'),
        ),
      ),
    );
  }
}

class SecondScreen extends StatelessWidget {
  const SecondScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Second Screen'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.pop(context);
          },
          child: const Text('Go Back'),
        ),
      ),
    );
  }
}


Working of the Example



  1. The application starts with HomeScreen.

  2. The user taps Open Second Screen.

  3. Navigator.push() adds SecondScreen to the navigation stack.

  4. SecondScreen becomes visible.

  5. The user taps Go Back.

  6. Navigator.pop() removes SecondScreen.

  7. The application returns to HomeScreen.




7. Navigation Flow Diagram


HomeScreen
    |
    | Navigator.push()
    ↓
SecondScreen
    |
    | Navigator.pop()
    ↓
HomeScreen



8. Navigator.of(context).push()


You can also access the Navigator using Navigator.of(context).


Navigator.of(context).push(
  MaterialPageRoute(
    builder: (context) => const SecondScreen(),
  ),
);

This is functionally similar to:


Navigator.push(
  context,
  MaterialPageRoute(
    builder: (context) => const SecondScreen(),
  ),
);

Both approaches access the Navigator associated with the provided BuildContext.




9. Using AppBar Back Button


When a new route is pushed using a standard Material route, Flutter generally provides an appropriate back navigation control in the AppBar. The user can also create a custom back button.


AppBar(
  title: const Text('Details'),
  leading: IconButton(
    icon: const Icon(Icons.arrow_back),
    onPressed: () {
      Navigator.pop(context);
    },
  ),
)

Here, tapping the arrow calls Navigator.pop(context) and returns to the previous route.




10. Navigating from a List to a Details Screen


A common real-world use case is opening a details page when the user taps an item in a list.


import 'package:flutter/material.dart';

class ProductListScreen extends StatelessWidget {
  const ProductListScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Products'),
      ),
      body: ListView(
        children: [
          ListTile(
            leading: const Icon(Icons.phone_android),
            title: const Text('Smartphone'),
            subtitle: const Text('View product details'),
            onTap: () {
              Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (context) => const ProductDetailsScreen(),
                ),
              );
            },
          ),
          ListTile(
            leading: const Icon(Icons.laptop),
            title: const Text('Laptop'),
            subtitle: const Text('View product details'),
            onTap: () {
              Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (context) => const ProductDetailsScreen(),
                ),
              );
            },
          ),
        ],
      ),
    );
  }
}

class ProductDetailsScreen extends StatelessWidget {
  const ProductDetailsScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Product Details'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.pop(context);
          },
          child: const Text('Back to Products'),
        ),
      ),
    );
  }
}




11. Passing Data to a New Screen


In real applications, you often need to send data from one screen to another. A simple approach is to pass the data through the destination screen's constructor.


Example


import 'package:flutter/material.dart';

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    const String productName = 'Flutter Course';

    return Scaffold(
      appBar: AppBar(
        title: const Text('Home'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) => ProductScreen(
                  name: productName,
                ),
              ),
            );
          },
          child: const Text('View Product'),
        ),
      ),
    );
  }
}

class ProductScreen extends StatelessWidget {
  final String name;

  const ProductScreen({
    super.key,
    required this.name,
  });

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Product'),
      ),
      body: Center(
        child: Text(
          'Product: $name',
          style: const TextStyle(fontSize: 22),
        ),
      ),
    );
  }
}


Here, the name value is passed from HomeScreen to ProductScreen.




12. Returning Data with Navigator.pop()


Navigator.pop() can also return a result to the previous screen.


Example


final result = await Navigator.push(
  context,
  MaterialPageRoute(
    builder: (context) => const SelectionScreen(),
  ),
);

The second screen can return a value:


Navigator.pop(context, 'Flutter selected');

The complete example is:


import 'package:flutter/material.dart';

class HomeScreen extends StatefulWidget {
  const HomeScreen({super.key});

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  String message = 'No selection';

  Future<void> openSelectionScreen() async {
    final result = await Navigator.push<String>(
      context,
      MaterialPageRoute(
        builder: (context) => const SelectionScreen(),
      ),
    );

    if (result != null) {
      setState(() {
        message = result;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Home'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(message),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: openSelectionScreen,
              child: const Text('Open Selection'),
            ),
          ],
        ),
      ),
    );
  }
}

class SelectionScreen extends StatelessWidget {
  const SelectionScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Selection'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.pop(context, 'Flutter selected');
          },
          child: const Text('Select Flutter'),
        ),
      ),
    );
  }
}


In this example, the first screen waits for the second screen to return a value. The second screen returns the value using Navigator.pop(context, result).




13. Navigator.push() vs Navigator.pop()









FeatureNavigator.push()Navigator.pop()
PurposeOpens a new routeCloses the current route
Stack operationAdds a routeRemoves a route
Typical useNavigate forwardNavigate back
Common syntaxNavigator.push(context, route)Navigator.pop(context)
Can return data?Can receive a returned result through awaitCan return a result using the second argument



14. Multiple Screens with Push and Pop


Suppose an application contains three screens:


Home
 ↓ push
Products
 ↓ push
Details

If the user calls Navigator.pop(context) from Details:


Home
 ↓
Products

If the user calls Navigator.pop(context) again:


Home

Every pop() removes the current top route from the stack.




15. Checking Whether a Route Can Be Popped


Before popping a route, you may need to check whether there is a previous route available.


if (Navigator.canPop(context)) {
  Navigator.pop(context);
}

Navigator.canPop(context) returns true when the Navigator can remove the current route and reveal another route.




16. pushReplacement()


Sometimes you do not want the user to return to the current screen. In such cases, pushReplacement() can replace the current route with a new route.


Navigator.pushReplacement(
  context,
  MaterialPageRoute(
    builder: (context) => const HomeScreen(),
  ),
);

A common example is moving from a Login screen to a Home screen after successful authentication so that the user does not simply return to the Login screen using the back navigation.




17. pushAndRemoveUntil()


pushAndRemoveUntil() pushes a new route and removes previous routes according to a condition.


Navigator.pushAndRemoveUntil(
  context,
  MaterialPageRoute(
    builder: (context) => const HomeScreen(),
  ),
  (route) => false,
);

This pattern can be useful when you want to navigate to a new destination and clear the previous navigation history.




18. Cupertino Navigation


For Cupertino-style applications, Flutter provides CupertinoPageRoute.


Navigator.push(
  context,
  CupertinoPageRoute(
    builder: (context) => const SecondScreen(),
  ),
);

The destination can still be closed using:


Navigator.pop(context);



19. Common Real-World Uses of Push and Pop










ScenarioNavigation Operation
Home → Product Detailspush()
Home → Profilepush()
Product Details → Product Listpop()
Settings → Previous Screenpop()
Login → Home without returning to LoginpushReplacement()
Checkout complete → Home and clear historypushAndRemoveUntil()



20. Common Mistakes


Mistake 1: Forgetting MaterialPageRoute


Navigator.push(
  context,
  const SecondScreen(),
);

The second argument of Navigator.push() should be a Route. Use MaterialPageRoute or another appropriate route implementation.


Correct Code


Navigator.push(
  context,
  MaterialPageRoute(
    builder: (context) => const SecondScreen(),
  ),
);

Mistake 2: Calling pop without the correct context


Make sure the BuildContext passed to the Navigator refers to the intended Navigator.


Mistake 3: Popping when there is no previous route


If necessary, check:


if (Navigator.canPop(context)) {
  Navigator.pop(context);
}

Mistake 4: Using push when replacement is required


If the previous screen should not remain in the navigation history, consider pushReplacement() instead of repeatedly pushing new routes.




21. Best Practices



  • Use Navigator.push() when you need to open a new screen.

  • Use Navigator.pop() when returning to the previous screen.

  • Use MaterialPageRoute for Material-style route navigation.

  • Use CupertinoPageRoute when Cupertino-style navigation behavior is appropriate.

  • Pass screen data through constructors when the destination screen needs specific data.

  • Use Navigator.pop(context, result) when a destination needs to return data.

  • Use Navigator.canPop(context) when a pop operation may not always be valid.

  • Use pushReplacement() when the current route should be replaced rather than kept in history.

  • Use pushAndRemoveUntil() when previous navigation history needs to be cleared according to a condition.

  • For applications with advanced deep-linking and routing requirements, consider Flutter's Router system or a routing package such as go_router.




22. Navigator Push and Pop Complete Example


import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Push and Pop Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(
          seedColor: Colors.blue,
        ),
        useMaterial3: true,
      ),
      home: const HomeScreen(),
    );
  }
}

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Home Screen'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) => const DetailsScreen(),
              ),
            );
          },
          child: const Text('Open Details'),
        ),
      ),
    );
  }
}

class DetailsScreen extends StatelessWidget {
  const DetailsScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Details Screen'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text(
              'Welcome to the Details Screen',
              style: TextStyle(fontSize: 20),
            ),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {
                Navigator.pop(context);
              },
              child: const Text('Go Back'),
            ),
          ],
        ),
      ),
    );
  }
}




23. Step-by-Step Execution of the Complete Example



  1. main() starts the Flutter application.

  2. HomeScreen is displayed first.

  3. The user taps Open Details.

  4. Navigator.push() creates and pushes a MaterialPageRoute.

  5. DetailsScreen is displayed.

  6. The user taps Go Back.

  7. Navigator.pop(context) removes the Details route.

  8. The user returns to HomeScreen.




24. Push and Pop Quick Revision











ConceptMeaningExample
NavigatorManages the navigation stackNavigator
RouteRepresents a screen/page in navigationMaterialPageRoute
push()Adds a new routeNavigator.push(...)
pop()Removes the current routeNavigator.pop(context)
canPop()Checks whether the current route can be poppedNavigator.canPop(context)
pushReplacement()Replaces the current routeNavigator.pushReplacement(...)
pushAndRemoveUntil()Pushes a route and removes routes according to a conditionNavigator.pushAndRemoveUntil(...)



25. Interview Questions


Q1. What is Navigator in Flutter?


Navigator is a Flutter widget that manages a stack of routes and provides methods for navigating between screens.


Q2. What does Navigator.push() do?


Navigator.push() adds a new route to the Navigator stack and displays the new screen.


Q3. What does Navigator.pop() do?


Navigator.pop() removes the current route from the Navigator stack and returns to the previous route.


Q4. What is MaterialPageRoute?


MaterialPageRoute is a Route implementation used for Material-style screen transitions.


Q5. How can you pass data back to the previous screen?


Use Navigator.pop(context, result) on the destination screen and await the result of Navigator.push() on the previous screen.


Q6. What is the difference between push() and pop()?


push() adds a new route to the stack, while pop() removes the current route from the stack.


Q7. What is pushReplacement()?


pushReplacement() replaces the current route with a new route instead of keeping the current route underneath it.




26. Practice Exercises



  1. Create a Home screen and Profile screen using Navigator.push() and Navigator.pop().

  2. Create a Product List screen and Product Details screen.

  3. Pass a product name from the List screen to the Details screen.

  4. Add a custom back button using Navigator.pop(context).

  5. Create a Settings screen and return to the Home screen using Navigator.pop().

  6. Create a selection screen that returns a selected value to the previous screen.

  7. Create Login and Home screens and use pushReplacement() after login.

  8. Create a checkout flow and use pushAndRemoveUntil() after checkout completion.




27. Key Takeaways



  • Flutter uses routes to represent application screens.

  • Navigator manages a stack of routes.

  • Navigator.push() opens a new screen by adding a route to the stack.

  • Navigator.pop() closes the current screen by removing the top route.

  • MaterialPageRoute can be used for Material-style navigation.

  • Navigator.pop(context, result) can return data to the previous screen.

  • Navigator.canPop(context) can be used to check whether a route can be popped.

  • pushReplacement() replaces the current route.

  • pushAndRemoveUntil() can be used to clear routes according to a condition.

  • For advanced navigation and deep-linking requirements, Flutter also provides Router-based navigation and supports routing packages such as go_router.




28. Official Flutter Documentation





29. Flutter Training Resources


Learn more about Flutter development and training through the following resources:



whatsapp