The imperative navigation means moving between screens by giving explicit commands to the Flutter navigator.
If you come from web development, you’re used to thinking about “URLs” and “links.” In Flutter (and native mobile development in general), the mental model is different.
Here, we don’t “go” to another page, but rather stack one screen on top of another.
To manage this, Flutter uses the Navigator widget. Think of it as a plate stack manager.
- Putting a plate on top = Going to a new screen (Push).
- Removing the top plate = Going back to the previous one (Pop).
Going to a New Screen: Navigator.push
To navigate forward, we need two things: the context (to know where we are) and the Route (the screen we want to go to, wrapped in an animation).
The basic syntax is:
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SegundaPantalla()),
);
What is MaterialPageRoute?
It is a widget that wraps your screen and adds the native transition animations of the operating system.
- On Android: The screen appears sliding from bottom to top and fading in (or Zoom, depending on the version).
- On iOS: The screen slides in from the right.
You don’t have to code the animation; Flutter detects the OS and chooses the correct one.
Going Back: Navigator.pop
To go back to the previous screen, we simply need to “destroy” the current screen and remove it from the stack.
Navigator.pop(context);
The Automatic “Back” Button
The best part of using the AppBar widget in your Scaffold is that Flutter is very smart.
If Flutter detects that the navigation stack has previous screens, it automatically adds a “Back arrow” button in the top-left corner of the AppBar.
When you press that arrow, Flutter executes Navigator.pop(context) for you. So, in most cases, you don’t even have to code the back button!
Complete Example
Let’s look at a functional example with two simple screens. Copy this into your editor and try it out.
import 'package:flutter/material.dart';
void main() => runApp(MaterialApp(home: PrimeraPantalla()));
// --- SCREEN 1 ---
class PrimeraPantalla extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('First Screen')),
body: Center(
child: ElevatedButton(
child: Text('Go to Second Screen'),
onPressed: () {
// FORWARD NAVIGATION (PUSH)
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SegundaPantalla()),
);
},
),
),
);
}
}
// --- SCREEN 2 ---
class SegundaPantalla extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
// Flutter will put the back arrow here automatically
appBar: AppBar(
title: Text('Second Screen'),
backgroundColor: Colors.orange,
),
body: Center(
child: ElevatedButton(
child: Text('Go back manually'),
onPressed: () {
// BACKWARD NAVIGATION (POP)
Navigator.pop(context);
},
),
),
);
}
}
A Common Mistake: push instead of pop
A very typical mistake is trying to “go back” using push. Imagine you are on Screen B and want to return to Screen A.
- Correct: You
pop. You remove B and A remains. - Incorrect: You
push(ScreenA). Now you have A -> B -> A.
If you do this many times, you’ll end up with an infinite stack (A -> B -> A -> B…) that will consume all the phone’s memory until the App closes.
Whenever you want to “go back”, use pop. Use push only when you want to “go forward” to something new.
Replacing Screens (pushReplacement)
Sometimes you don’t want to stack, but rather replace. A classic example is the Login screen.
Once the user logs in and goes to the “Home”, we do not want them to be able to press “Back” and return to the Login. We want the Login to disappear from memory.
For that, we use pushReplacement:
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => PantallaHome()),
);
This “removes” the current screen (Login) and “puts” the new one (Home) in the same move. The stack stays clean.