← Flutter & Dart

Advanced Flutter

How does navigation work in Flutter, and what's the difference between `push` and `pushReplacement`?

Show answer — try answering out loud first

Flutter navigates with a Navigator that manages a stack of routes. push adds a screen on top of the current one (you can go back with pop). pushReplacement replaces the current screen with another (you can't go back to the previous one). There's also declarative navigation (Navigator 2.0 and packages like go_router) for more complex cases.

Think of navigation as a stack of cards: the top card is the screen you see.

  • push: you put a new card on top. pop removes it and you go back to the previous one.
  • pushReplacement: you swap the top card for another one. The previous one disappears from the stack, so there's no "going back".
  • pushNamed: you navigate using registered route names ('/detail'), handy for centralizing routes.
  • pop with a result: when going back you can return a value to the previous screen.

For apps with complex navigation (deep links, web, navigation state), use the declarative approach with go_router, which is the one currently recommended by the Flutter team.

import 'package:flutter/material.dart';

// Go to a new screen and be able to come back
void openDetail(BuildContext context) {
  Navigator.of(context).push(
    MaterialPageRoute(builder: (_) => const Detail()),
  );
}

// Replace the current one (for example, after a login: you don't want to go back to the login)
void goToHome(BuildContext context) {
  Navigator.of(context).pushReplacement(
    MaterialPageRoute(builder: (_) => const Home()),
  );
}

// Go back returning a result
void goBackWithData(BuildContext context) {
  Navigator.of(context).pop("result");
}

class Detail extends StatelessWidget {
  const Detail({super.key});
  @override
  Widget build(BuildContext context) => const Scaffold();
}

class Home extends StatelessWidget {
  const Home({super.key});
  @override
  Widget build(BuildContext context) => const Scaffold();
}

Using push when you should have used pushReplacement, typically after a login or a splash. If you push to the home after login, the user can press "back" and return to the login screen, which makes no sense. Another mistake: trying to pop when there's nothing left below in the stack (you'd close the app by accident).

"Flutter uses a Navigator that manages a stack of routes. push adds a screen on top and I can go back with pop; pushReplacement swaps the current screen for another with no way back, which is what I use after a login or a splash. I can navigate by name with pushNamed and return results with pop. For complex navigation, deep links, or web, I use the declarative approach with go_router, which is the currently recommended one."

Quick challenge

After a successful login you want to take the user to the home and make sure the "back" button doesn't return them to the login. Which method do you use?

See answer

pushReplacement (or pushReplacementNamed). It replaces the login screen with the home in the stack, so the login disappears and the "back" button no longer returns to it. If you wanted to clear the entire previous stack (for example, several screens), you'd use pushAndRemoveUntil.