Navigation Basics
Navigation Basics Interview with follow-up questions
1. What is the role of the Navigator class in Flutter?
Navigator manages a stack of routes (screens). It implements the imperative navigation model: you push a route to go forward and pop to go back, and it animates the transitions and wires up the system/back-button behavior.
Core operations:
Navigator.push(context, route)— put a new screen on top.Navigator.pop(context, [result])— remove the top screen and optionally return a value to the caller.pushReplacement,pushAndRemoveUntil,popUntil— replace the current screen, reset the stack (e.g. after login), or pop back to a specific point.- Named-route variants (
pushNamed, etc.) when you register routes by string.
final result = await Navigator.push(
context,
MaterialPageRoute(builder: (_) => const DetailPage()),
);
// DetailPage calls Navigator.pop(context, value) to send `result` back.
The framing interviewers expect in 2026: Navigator (the "1.0" imperative API) is still perfect for simple apps and within-flow pushes, and it's the engine everything sits on. But for production apps with web URLs, deep links, and auth redirects, you don't drive Navigator directly anymore — you use go_router (maintained by the Flutter team), which is declarative, built on the Router/Navigator 2.0 API, and parses URLs into the navigation stack for you. Hand-rolling raw Navigator 2.0 (RouterDelegate/RouteInformationParser) is now rare — go_router replaced that boilerplate.
Gotcha: calling Navigator.of(context) with a context above the Navigator (e.g. the same widget that builds MaterialApp) fails — use a Builder or a child context.
Follow-up 1
How does the Navigator class manage app routes?
The Navigator class manages app routes by maintaining a stack of Route objects. Each Route represents a screen or page in the app. When a new screen is pushed onto the stack, a new Route object is created and added to the top of the stack. When a screen is popped off the stack, the corresponding Route object is removed from the stack. The Navigator class also provides methods for replacing a screen with a new one, clearing the entire stack, and navigating to a specific screen by name.
Follow-up 2
Can you explain the push and pop methods in the Navigator class?
The push method in the Navigator class is used to push a new screen onto the navigation stack. It takes a Route object as a parameter, which represents the screen to be pushed. The new screen becomes the topmost screen on the stack.
Example:
Navigator.push(context, MaterialPageRoute(builder: (context) => MyScreen()));
The pop method in the Navigator class is used to pop the topmost screen off the navigation stack. It removes the current screen from the stack and returns to the previous screen.
Example:
Navigator.pop(context);
Follow-up 3
What is the role of the context in the Navigator class?
The context parameter in the Navigator class is used to provide the current build context to the navigation methods. It is required for performing navigation operations, such as pushing or popping screens. The context is typically obtained from the build method of a widget and is used to access the Navigator object associated with the current widget tree.
Example:
Navigator.push(context, MaterialPageRoute(builder: (context) => MyScreen()));
2. What is the difference between named routes and anonymous routes in Flutter?
Both create routes on the Navigator stack; the difference is whether the route is registered under a string name.
Named routes are declared up front and navigated to by a string identifier:
MaterialApp(
routes: {
'/': (_) => const HomePage(),
'/details': (_) => const DetailsPage(),
},
);
Navigator.pushNamed(context, '/details', arguments: item);
They centralize your route table, make deep-link/URL mapping straightforward, and let any screen navigate by name without importing the target widget.
Anonymous (inline) routes are built on the spot with a MaterialPageRoute and a builder:
Navigator.push(
context,
MaterialPageRoute(builder: (_) => DetailsPage(item: item)),
);
These are great for local, one-off pushes and let you pass typed arguments directly through the constructor — which is type-safe, unlike settings.arguments (an untyped Object? you must cast).
The 2026 framing: named routes via a static routes: map don't scale well to dynamic paths, parameters, and deep links — that's why teams reach for onGenerateRoute for more control, or more commonly go_router, which gives you declarative, URL-based, typed routes that replace both styles for production apps.
Gotcha: settings.arguments is untyped, so a mismatched cast is a runtime crash — prefer constructor passing (inline routes or go_router's typed routes) when you can.
Follow-up 1
Can you provide an example of how to use named routes?
Sure! Here's an example of how to use named routes in Flutter:
First, define the named routes in the MaterialApp widget:
MaterialApp(
routes: {
'/': (context) => HomeScreen(),
'/details': (context) => DetailsScreen(),
},
)
Then, you can navigate to a named route using the Navigator widget:
Navigator.pushNamed(context, '/details');
This will push the DetailsScreen onto the navigation stack and display it on the screen.
Follow-up 2
When would you prefer to use anonymous routes?
Anonymous routes are preferred in situations where the navigation flow is simple and the route does not need to be accessed from multiple places in the app. They are useful for one-time or temporary screens that do not require a unique name. For example, a login screen or a confirmation dialog can be implemented as an anonymous route.
Follow-up 3
How do you pass arguments to a named route?
To pass arguments to a named route in Flutter, you can use the 'arguments' parameter of the Navigator.pushNamed method. Here's an example:
Navigator.pushNamed(context, '/details', arguments: {'id': 1, 'name': 'John'});
In the receiving screen, you can access the arguments using the ModalRoute.of(context).settings.arguments property:
class DetailsScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
final Map arguments = ModalRoute.of(context).settings.arguments;
final int id = arguments['id'];
final String name = arguments['name'];
// Use the arguments in the screen
...
}
}
3. How does Flutter handle deep linking?
Correction: flutter_linkify only auto-detects and styles URLs inside text — it has nothing to do with deep linking. Deep linking means the OS launching your app at a specific in-app location from an external URL.
How deep linking actually works in Flutter:
- Platform configuration first. You declare the links the OS should route to your app: Android App Links (intent filters +
assetlinks.json) and iOS Universal Links (associated domains +apple-app-site-association), or custom URL schemes. Flutter's deep-linking support is enabled by default in current versions. - The app receives an initial route / route information for the incoming URL, and the Router parses it into your navigation stack.
- In practice you use
go_router(the Flutter-team package) to handle this cleanly: it maps incoming URLs to routes, supports path/query parameters, nested navigation, andredirectfor auth-gated links.
final router = GoRouter(
routes: [
GoRoute(path: '/', builder: (_, __) => const HomePage()),
GoRoute(
path: '/product/:id', // matches https://example.com/product/42
builder: (_, state) => ProductPage(id: state.pathParameters['id']!),
),
],
redirect: (context, state) => isLoggedIn ? null : '/login',
);
MaterialApp.router(routerConfig: router);
Gotchas: deep links won't work without the native platform setup (assetlinks.json / AASA) — a pure-Dart solution can't grant the OS association. For runtime notification/custom-scheme links you may still use a plugin like app_links, but routing them into screens is go_router's job. Hand-rolling Navigator 2.0 for this is now rare.
Follow-up 1
What are some use cases for deep linking?
Some use cases for deep linking in Flutter include:
Opening a specific screen or view within the app when a user clicks on a link from another app or website.
Passing data or parameters to the app through the deep link, such as user authentication tokens or search queries.
Implementing referral programs or sharing features, where users can share deep links that provide rewards or discounts.
Integrating with external services or APIs, such as payment gateways or social media platforms, by handling deep links from these services.
Follow-up 2
What are the challenges in implementing deep linking?
Some challenges in implementing deep linking in Flutter include:
Handling different deep link formats and protocols, as different platforms and services may use different formats for deep links.
Ensuring that the app can handle deep links in a consistent and reliable manner, even when the app is not running or in the background.
Handling edge cases and error scenarios, such as invalid or malformed deep links, and providing appropriate error handling and feedback to the user.
Testing and debugging deep linking functionality across different platforms and devices, as deep linking behavior may vary.
Follow-up 3
How can deep linking improve user experience?
Deep linking can improve user experience in Flutter apps in several ways:
Seamless navigation: Deep linking allows users to seamlessly navigate to specific screens or views within the app, reducing the need for manual navigation and improving the overall user flow.
Personalization: Deep linking can be used to pass personalized data or parameters to the app, allowing for a more tailored and customized user experience.
Integration with external services: Deep linking enables integration with external services or APIs, such as payment gateways or social media platforms, providing a more seamless and integrated user experience.
Simplified sharing and referral: Deep linking can simplify the sharing of app content or referral programs, allowing users to easily share deep links that provide rewards or discounts to their friends and contacts.
4. What is the purpose of the MaterialPageRoute class in Flutter?
MaterialPageRoute is a PageRoute that builds a full-screen route and animates it in with Material Design transitions — on Android a bottom-up slide/fade, with the platform-appropriate behavior elsewhere. You pass it to Navigator.push with a builder that returns the destination screen.
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const DetailPage(),
settings: const RouteSettings(name: '/detail'), // optional, for logging/args
fullscreenDialog: true, // makes it a modal page with a close (X) affordance
),
);
What it gives you beyond just "showing a screen":
- The transition animation and a back/up gesture appropriate to the platform.
- A modal barrier and proper route lifecycle (it's pushed/popped on the
Navigatorstack, integrates with the system back button). fullscreenDialog: trueswitches to the modal-dialog style (vertical slide + close icon) for create/edit flows.
Contrast for the follow-up:
CupertinoPageRoutegives the iOS-style horizontal slide with edge swipe-back.- For a custom animation, use
PageRouteBuilderand supply your owntransitionsBuilder.
2026 framing: you instantiate MaterialPageRoute directly with imperative Navigator. With go_router you usually specify transitions via MaterialPage/CupertinoPage/CustomTransitionPage in the route's pageBuilder instead, so you rarely new a MaterialPageRoute by hand in a router-based app.
Follow-up 1
How does MaterialPageRoute handle transitions?
The MaterialPageRoute class handles transitions by providing built-in animations when navigating between screens. By default, it uses a slide transition animation, but you can customize the transition by specifying a different animation type or duration.
Follow-up 2
What are the key properties of MaterialPageRoute?
The key properties of the MaterialPageRoute class include:
builder: A callback function that returns the widget tree for the route.settings: An optional RouteSettings object that can be used to pass data to the route.fullscreenDialog: A boolean value that indicates whether the route should be displayed as a fullscreen dialog.maintainState: A boolean value that indicates whether the state of the previous route should be maintained when navigating to the new route.
Follow-up 3
Can you provide an example of how to use MaterialPageRoute?
Sure! Here's an example of how to use the MaterialPageRoute class:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home'),
),
body: Center(
child: RaisedButton(
child: Text('Go to Details'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => DetailsPage()),
);
},
),
),
);
}
}
class DetailsPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Details'),
),
body: Center(
child: RaisedButton(
child: Text('Go back'),
onPressed: () {
Navigator.pop(context);
},
),
),
);
}
}
5. How can you handle back navigation in Flutter?
Heads-up: WillPopScope is deprecated (after Flutter 3.12) because it doesn't support Android's predictive-back gesture. Use PopScope instead — that's the answer interviewers expect in 2026.
The difference is structural: WillPopScope.onWillPop was async and decided at pop time, which can't drive a predictive-back animation. PopScope decides ahead of time with a canPop boolean and notifies you via onPopInvokedWithResult after the attempt.
PopScope(
canPop: !_hasUnsavedChanges, // decided up front
onPopInvokedWithResult: (bool didPop, Object? result) async {
if (didPop) return; // the pop already happened
final leave = await _confirmDiscard(context);
if (leave && context.mounted) {
Navigator.pop(context); // perform the pop yourself
}
},
child: YourWidget(),
);
Key points:
- Set
canPop: falseto intercept back (system button, app-bar back, predictive-back gesture). When intercepted,didPopisfalseand you decide whether to pop manually. - Set
canPop: trueto allow back normally (still useful for observing the pop). onPopInvokedWithResultreplaces the olderonPopInvoked; it also surfaces the route result.
Gotchas: because canPop is evaluated up front, you must keep it in sync with your state (e.g. toggle it when the form becomes dirty). With go_router, prefer PopScope too, or use the router's redirect for navigation guards. Don't reach for the deprecated WillPopScope in new code.
Follow-up 1
What is the role of the WillPopScope widget?
The WillPopScope widget in Flutter is used to handle back navigation. It allows you to intercept the back button press and perform custom actions before actually navigating back.
The WillPopScope widget takes an onWillPop callback as a parameter. This callback will be called when the back button is pressed. If the callback returns true, the back navigation will be allowed, otherwise, it will be blocked.
You can use the WillPopScope widget to handle scenarios like showing a confirmation dialog before navigating back or preventing the user from accidentally navigating back.
Here's an example of how to use the WillPopScope widget:
WillPopScope(
onWillPop: () async {
// Perform custom actions here
// Return true to allow back navigation, false to block it
return true;
},
child: YourWidget(),
)
Follow-up 2
How can you override the back button in Flutter?
To override the back button in Flutter, you can use the WillPopScope widget and the Navigator.pop method.
First, wrap your widget tree with a WillPopScope widget and provide an onWillPop callback. This callback will be called when the back button is pressed. If the callback returns true, the back navigation will be allowed, otherwise, it will be blocked.
Inside the onWillPop callback, you can perform custom actions and then call Navigator.pop to navigate back. By default, Navigator.pop will navigate back to the previous route in the navigation stack.
Here's an example of how to override the back button:
WillPopScope(
onWillPop: () async {
// Perform custom actions here
// Return true to allow back navigation, false to block it
Navigator.pop(context);
return false;
},
child: YourWidget(),
)
Follow-up 3
What is the difference between Navigator.pop and Navigator.popUntil?
In Flutter, Navigator.pop and Navigator.popUntil are both methods used for navigating back, but they have different behaviors.
Navigator.pop: This method pops the current route from the navigation stack and returns to the previous route. It takes an optional result parameter that can be used to pass data back to the previous route.
Navigator.popUntil: This method pops routes from the navigation stack until a given condition is met. It takes a predicate parameter that defines the condition. The predicate is a function that takes a Route and returns a boolean value. The popping stops when the predicate returns true.
Here's an example of how to use Navigator.pop and Navigator.popUntil:
// Using Navigator.pop
Navigator.pop(context);
// Using Navigator.popUntil
Navigator.popUntil(context, (route) => route.isFirst);
Live mock interview
Mock interview: Navigation Basics
- Read your scene and goals
- Talk it out; goals tick off live
- Get a score and stronger lines
Your voice and your AI key never touch our servers; the key stays in this browser and is sent only to Google. Only your round scores are saved to track progress.