Popular Searches
Popular Course Categories
Popular Courses

Opening and closing screens

Opening and closing screens

Flutter Navigation & Screens


Opening and Closing Screens in Flutter


Flutter applications commonly contain multiple screens, such as Home, Login, Profile, Product Details, Settings, Cart, and Checkout screens. Moving from one screen to another is called navigation. In Flutter, screens and pages are represented as routes, and the Navigator manages these routes as a stack. :contentReference[oaicite:0]{index=0}


Opening a screen generally means adding a new route to the navigation stack, while closing a screen generally means removing the current route and returning to the previous screen.




1. What Does Opening and Closing a Screen Mean?


When a Flutter application starts, it displays an initial screen. When the user taps a button or another interactive element, the application may open another screen.


For example:


Home Screen
     ↓
Product Screen
     ↓
Product Details Screen

When the user moves from Home Screen to Product Screen, a new route is pushed onto the Navigator stack. When the user closes Product Screen, its route is popped from the stack and the application returns to Home Screen.




2. Understanding Routes in Flutter


Flutter uses the term route for a screen or page in the navigation system. A route is represented by a widget and is managed by the Navigator. :contentReference[oaicite:1]{index=1}









TermMeaning
ScreenThe visible page of the application.
RouteA Flutter representation of a screen/page used by the navigation system.
NavigatorManages the stack of routes.
PushAdds a new route to the stack.
PopRemoves the current route from the stack.



3. Opening a New Screen with Navigator.push()


The most basic way to open another screen is to use Navigator.push(). The push() method adds a new route to the Navigator's stack. :contentReference[oaicite:2]{index=2}


Basic Syntax


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

Here:



  • Navigator.push() starts navigation to a new screen.

  • context identifies the current location in the widget tree.

  • MaterialPageRoute creates the route.

  • builder returns the widget that should be displayed.

  • SecondScreen is the destination screen.




4. Closing the Current Screen with Navigator.pop()


To close the current screen and return to the previous screen, use Navigator.pop().


Basic Syntax


Navigator.pop(context);

The pop() method removes the current route from the Navigator stack. :contentReference[oaicite:3]{index=3}


For example, if the stack is:


[HomeScreen]
[DetailsScreen]

After calling:


Navigator.pop(context);

The stack becomes:


[HomeScreen]



5. Opening and Closing Screens: Basic Example


The following example demonstrates opening a second screen from a first screen and then closing the second 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: 'Opening and Closing Screens',
      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 Screen'),
        ),
      ),
    );
  }
}

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: ElevatedButton(
          onPressed: () {
            Navigator.pop(context);
          },
          child: const Text('Close Screen'),
        ),
      ),
    );
  }
}


How This Example Works



  1. The application starts on HomeScreen.

  2. The user taps Open Details Screen.

  3. Navigator.push() opens DetailsScreen.

  4. The Details Screen is added to the navigation stack.

  5. The user taps Close Screen.

  6. Navigator.pop() removes the Details Screen.

  7. The user returns to the Home Screen.




6. Understanding the Navigation Stack


The Navigator maintains a stack of routes. The last route added to the stack is the route currently displayed.


Initial State


Navigator Stack
----------------
HomeScreen

After Opening Details Screen


Navigator Stack
----------------
HomeScreen
DetailsScreen  ← Current Screen

After Closing Details Screen


Navigator Stack
----------------
HomeScreen  ← Current Screen

This behavior follows the LIFO (Last In, First Out) principle.




7. Opening Multiple Screens


You can open multiple screens one after another.


Home
 ↓ push
Products
 ↓ push
Details
 ↓ push
Checkout

The Navigator stack becomes:


Home
Products
Details
Checkout

If Checkout is closed:


Home
Products
Details

If Details is then closed:


Home
Products

Each pop() removes the current route.




8. Opening a Screen from a List Item


A common application pattern is opening a details screen 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('Tap to view details'),
            onTap: () {
              Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (context) => const ProductDetailsScreen(
                    productName: 'Smartphone',
                  ),
                ),
              );
            },
          ),
          ListTile(
            leading: const Icon(Icons.laptop),
            title: const Text('Laptop'),
            subtitle: const Text('Tap to view details'),
            onTap: () {
              Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (context) => const ProductDetailsScreen(
                    productName: 'Laptop',
                  ),
                ),
              );
            },
          ),
        ],
      ),
    );
  }
}

class ProductDetailsScreen extends StatelessWidget {
  final String productName;

  const ProductDetailsScreen({
    super.key,
    required this.productName,
  });

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Product Details'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              productName,
              style: const TextStyle(
                fontSize: 24,
                fontWeight: FontWeight.bold,
              ),
            ),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {
                Navigator.pop(context);
              },
              child: const Text('Close'),
            ),
          ],
        ),
      ),
    );
  }
}


Here, the selected product name is passed to the details screen through its constructor.




9. Opening a Screen with MaterialPageRoute


MaterialPageRoute creates a route with Material-style transition behavior.


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

When the user taps the button, Flutter creates the route and displays the Profile Screen.




10. Opening a Screen with CupertinoPageRoute


Flutter also provides CupertinoPageRoute for Cupertino-style navigation.


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

Both MaterialPageRoute and CupertinoPageRoute are route implementations that can be used with Navigator.push(). :contentReference[oaicite:4]{index=4}




11. Using Navigator.of(context)


Another common syntax is to explicitly obtain the Navigator using Navigator.of(context).


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

To close the screen:


Navigator.of(context).pop();

This provides explicit access to the Navigator associated with the supplied BuildContext.




12. Closing a Screen Using the AppBar Back Button


Flutter's Material navigation commonly provides a back button in the AppBar when there is a previous route. You can also create your own custom back button.


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

When the arrow is tapped, Navigator.pop(context) closes the current screen.




13. Checking Whether a Screen Can Be Closed


Sometimes you may want to check whether the current route can be popped before calling pop().


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

This is useful when the same widget can be displayed in different navigation situations.




14. Opening a Screen and Receiving Data Back


A screen can return a value when it closes. The calling screen can wait for that result using await.


First Screen


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

if (result != null) {
  print(result);
}


Second Screen


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

The value passed as the second argument of pop() is returned to the previous screen.




15. Complete Example with Returning Data


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: 'Screen Navigation',
      home: const HomeScreen(),
    );
  }
}

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

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

class _HomeScreenState extends State<HomeScreen> {
  String selectedItem = 'Nothing selected';

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

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

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

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'),
        ),
      ),
    );
  }
}


Flow


Home Screen
     ↓
Navigator.push()
     ↓
Selection Screen
     ↓
Navigator.pop(context, result)
     ↓
Home Screen receives result



16. Closing a Screen Without Returning Data


If you only want to close the current screen, use:


Navigator.pop(context);

No result is returned to the previous screen.




17. Closing a Screen and Returning Data


If you want to send information back while closing the screen, use:


Navigator.pop(context, 'Success');

The previous screen can receive the result:


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



18. Replacing the Current Screen


Sometimes opening another screen is not enough because 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. :contentReference[oaicite:5]{index=5}


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

A common example is moving from Login Screen to Home Screen after successful login.


Login Screen
     ↓
pushReplacement()
     ↓
Home Screen



19. Opening a Screen and Removing Previous Screens


pushAndRemoveUntil() can add a new route and remove previous routes until a specified condition is met. :contentReference[oaicite:6]{index=6}


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

This can be useful after completing a flow where previous screens should no longer remain in the navigation history.




20. Login and Logout Example


Login to Home


ElevatedButton(
  onPressed: () {
    Navigator.pushReplacement(
      context,
      MaterialPageRoute(
        builder: (context) => const HomeScreen(),
      ),
    );
  },
  child: const Text('Login'),
)

Home to Login


ElevatedButton(
  onPressed: () {
    Navigator.pushAndRemoveUntil(
      context,
      MaterialPageRoute(
        builder: (context) => const LoginScreen(),
      ),
      (route) => false,
    );
  },
  child: const Text('Logout'),
)

This pattern prevents the user from simply navigating backward through the previous authenticated screens after logout.




21. Opening and Closing Dialogs


Dialogs also use Flutter's navigation system. For example, showDialog() displays a dialog route.


showDialog(
  context: context,
  builder: (context) {
    return AlertDialog(
      title: const Text('Delete Item'),
      content: const Text(
        'Are you sure you want to delete this item?',
      ),
      actions: [
        TextButton(
          onPressed: () {
            Navigator.pop(context);
          },
          child: const Text('Cancel'),
        ),
        TextButton(
          onPressed: () {
            Navigator.pop(context);
          },
          child: const Text('Delete'),
        ),
      ],
    );
  },
);

Here, Navigator.pop(context) closes the dialog route.




22. Common Mistakes


Mistake 1: Passing a Widget Directly to push()


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

This is incorrect because Navigator.push() expects a Route, not simply a destination widget.


Correct Approach


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

Mistake 2: Calling pop() Without the Correct Context


Make sure the supplied BuildContext refers to the intended Navigator.


Mistake 3: Popping a Route When No Previous Route Exists


When necessary, check:


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

Mistake 4: Repeatedly Using push() When Replacement Is Required


If the current screen should not remain in the navigation history, consider using pushReplacement().




23. Best Practices



  • Use Navigator.push() to open a new screen in simple navigation flows.

  • Use Navigator.pop() to close the current screen and return to the previous route.

  • Use MaterialPageRoute for Material-style route navigation.

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

  • Use constructors to pass required data to destination screens.

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

  • Use Navigator.canPop(context) when you need to verify that a route can be removed.

  • Use pushReplacement() when the current screen should be replaced.

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

  • For applications with advanced deep-linking and routing requirements, consider Router-based navigation or a routing package such as go_router. :contentReference[oaicite:7]{index=7}




24. Opening vs Closing a Screen









FeatureOpening ScreenClosing Screen
Main methodNavigator.push()Navigator.pop()
Stack operationAdds a routeRemoves a route
Navigation directionUsually forwardUsually backward
Common useHome → DetailsDetails → Home
Can return data?Can await a resultCan provide a result



25. Push, Pop and Replacement Comparison








MethodPurposeEffect on Stack
push()Open a new screenAdds a route
pop()Close current screenRemoves the top route
pushReplacement()Replace current screenReplaces the top route
pushAndRemoveUntil()Open a screen and remove routesAdds a route and removes matching previous routes



26. Real-World Navigation Flow


Login Screen
     |
     | pushReplacement()
     ↓
Home Screen
     |
     | push()
     ↓
Product List
     |
     | push()
     ↓
Product Details
     |
     | pop()
     ↓
Product List
     |
     | pop()
     ↓
Home Screen

This demonstrates how different navigation operations can be combined to create a complete application flow.




27. Interview Questions


Q1. What is a route in Flutter?


A route represents a screen or page in Flutter's navigation system.


Q2. How do you open another screen in Flutter?


Use Navigator.push() with an appropriate route such as MaterialPageRoute.


Q3. How do you close the current screen?


Use Navigator.pop(context).


Q4. What happens when Navigator.push() is called?


A new route is added to the Navigator's route stack and becomes the current screen.


Q5. What happens when Navigator.pop() is called?


The current route is removed from the Navigator stack, revealing the previous route.


Q6. How can a screen return data?


Use Navigator.pop(context, result) and receive the result by awaiting Navigator.push().


Q7. What is pushReplacement()?


It replaces the current route with another route instead of keeping the current route underneath it.


Q8. What is pushAndRemoveUntil()?


It pushes a new route and removes previous routes until a supplied condition is satisfied.




28. Practice Exercises



  1. Create a Home Screen and Details Screen.

  2. Add an ElevatedButton that opens the Details Screen using Navigator.push().

  3. Add a Back button that closes the Details Screen using Navigator.pop().

  4. Create a Product List and Product Details application.

  5. Pass the product name from the List Screen to the Details Screen.

  6. Create a Selection Screen that returns a selected value to the Home Screen.

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

  8. Create a Logout button that clears the navigation history using pushAndRemoveUntil().

  9. Create a dialog and close it using Navigator.pop().

  10. Create a three-screen application and observe how the Navigator stack changes after every push and pop operation.




29. Quick Revision










ConceptCodePurpose
Open screenNavigator.push()Add a new route
Close screenNavigator.pop()Remove current route
Check popNavigator.canPop()Check whether current route can be popped
Replace screenNavigator.pushReplacement()Replace current route
Clear navigation historyNavigator.pushAndRemoveUntil()Add route and remove previous routes
Return dataNavigator.pop(context, result)Send a result back



30. Key Takeaways



  • Flutter uses routes to represent application screens.

  • The Navigator manages routes using a navigation stack.

  • Navigator.push() is commonly used to open a new screen.

  • Navigator.pop() is commonly used to close the current screen.

  • MaterialPageRoute can be used to create Material-style routes.

  • CupertinoPageRoute can be used for Cupertino-style routes.

  • A screen can return data with Navigator.pop(context, result).

  • pushReplacement() replaces the current route.

  • pushAndRemoveUntil() can be used to navigate while removing previous routes.

  • For simple navigation, Navigator provides an imperative push/pop API; advanced applications with deep-linking requirements can use Router-based navigation or a routing package. :contentReference[oaicite:8]{index=8}




31. Official Flutter Documentation





32. Flutter Training Resources


For more information about Flutter training and course learning resources, visit:



whatsapp