flutter-navegacion-basica-push-pop

Navegación imperativa en Flutter con Navigator

  • 3 min

La navegación imperativa es moverse entre pantallas dando órdenes explícitas al navegador de Flutter.

Si vienes del desarrollo web, estarás acostumbrado a pensar en “URLs” y “enlaces”. En Flutter (y en el desarrollo móvil nativo en general), el modelo mental es diferente.

Aquí no “vamos” a otra página, sino que apilamos una pantalla encima de otra.

Para gestionar esto, Flutter utiliza el widget Navigator. Imagínalo como un gestor de una pila de platos.

  • Poner un plato encima = Ir a una pantalla nueva (Push).
  • Quitar el plato de arriba = Volver a la anterior (Pop).

Ir a una nueva pantalla: Navigator.push

Para navegar hacia adelante, necesitamos dos cosas: el context (para saber dónde estamos) y la Route (la pantalla a la que queremos ir envuelta en una animación).

La sintaxis básica es:

Navigator.push(
  context,
  MaterialPageRoute(builder: (context) => SegundaPantalla()),
);
Copied!

¿Qué es MaterialPageRoute?

Es un widget que envuelve a tu pantalla y le añade las animaciones de transición nativas del sistema operativo.

  • En Android: La pantalla aparece deslizándose de abajo a arriba y desvaneciéndose (o Zoom, según versión).
  • En iOS: La pantalla se desliza desde la derecha.

No tienes que programar la animación, Flutter detecta el SO y elige la correcta.

Volver atrás: Navigator.pop

Para volver a la pantalla anterior, simplemente tenemos que “destruir” la pantalla actual y quitarla de la pila.

Navigator.pop(context);
Copied!

El botón “Atrás” automático

Lo mejor de usar el widget AppBar en tu Scaffold es que Flutter es muy inteligente.

Si Flutter detecta que la pila de navegación tiene pantallas previas, automáticamente añade un botón de “Flecha atrás” en la esquina superior izquierda del AppBar.

Al pulsar esa flecha, Flutter ejecuta Navigator.pop(context) por ti. Así que, en la mayoría de los casos, ¡no tienes ni que programar el botón de volver!

Ejemplo completo

Vamos a ver un ejemplo funcional con dos pantallas simples. Copia esto en tu editor y pruébalo.

import 'package:flutter/material.dart';

void main() => runApp(MaterialApp(home: PrimeraPantalla()));

// --- PANTALLA 1 ---
class PrimeraPantalla extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Primera Pantalla')),
      body: Center(
        child: ElevatedButton(
          child: Text('Ir a la Segunda Pantalla'),
          onPressed: () {
            // NAVEGACIÓN HACIA ADELANTE (PUSH)
            Navigator.push(
              context,
              MaterialPageRoute(builder: (context) => SegundaPantalla()),
            );
          },
        ),
      ),
    );
  }
}

// --- PANTALLA 2 ---
class SegundaPantalla extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      // Flutter pondrá aquí la flecha de volver automáticamente
      appBar: AppBar(
        title: Text('Segunda Pantalla'),
        backgroundColor: Colors.orange,
      ),
      body: Center(
        child: ElevatedButton(
          child: Text('Volver manualmente'),
          onPressed: () {
            // NAVEGACIÓN HACIA ATRÁS (POP)
            Navigator.pop(context);
          },
        ),
      ),
    );
  }
}
Copied!

Un error común: push en vez de pop

Un error muy típico es intentar “volver” usando push. Imagina que estás en la Pantalla B y quieres volver a la Pantalla A.

  • Correcto: Haces pop. Quitas B y queda A.
  • Incorrecto: Haces push(PantallaA). Ahora tienes A -> B -> A.

Si haces esto muchas veces, acabarás con una pila infinita (A -> B -> A -> B…) que consumirá toda la memoria del teléfono hasta que la App se cierre.

Siempre que quieras “volver”, usa pop. Usa push solo cuando quieras “avanzar” a algo nuevo.

Reemplazar pantallas (pushReplacement)

A veces no quieres apilar, sino sustituir. Un ejemplo clásico es la pantalla de Login.

Una vez que el usuario se loguea y va al “Home”, no queremos que pueda pulsar “Atrás” y volver al Login. Queremos que el Login desaparezca de la memoria.

Para eso usamos pushReplacement:

Navigator.pushReplacement(
  context,
  MaterialPageRoute(builder: (context) => PantallaHome()),
);
Copied!

Esto “quita” la pantalla actual (Login) y “pone” la nueva (Home) en el mismo movimiento. La pila se mantiene limpia.