certmundo.
es‑mx

6 min de lectura

¿Cómo navegar entre pantallas en Flutter?

La navegación en Flutter funciona con un sistema de pila (stack) que usa Navigator.push para abrir una nueva pantalla y Navigator.pop para regresar a la anterior.

En esta lección aprenderás a moverte entre pantallas de forma programática. También verás cómo organizar la navegación con rutas con nombre, lo cual es esencial en apps más grandes como una tienda estilo Liverpool o un catálogo de productos de FEMSA.


El sistema de navegación de Flutter

Flutter maneja las pantallas como una pila de rutas. Imagina una torre de cartas: agregas una carta encima (push) o la quitas (pop).

Cada pantalla que agregas cubre la anterior. Cuando haces pop, la pantalla superior desaparece y vuelves a la de abajo.

Este modelo se llama Navigator stack y es el núcleo de toda la navegación en Flutter.


Sintaxis de Navigator.push y Navigator.pop

Navigator.push recibe el contexto actual y una Route. La ruta más común es MaterialPageRoute.

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

Navigator.pop simplemente cierra la pantalla actual y regresa a la anterior.

Navigator.pop(context);

Ambos métodos necesitan el BuildContext para saber en qué parte del árbol de widgets están.


Ejemplo 1: Navegación básica entre dos pantallas

Supón que tienes una app de catálogo para una tienda como Liverpool. La pantalla principal muestra una lista de productos. Al tocar un producto, navegas al detalle.

// Pantalla principal
class PantallaInicio extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Catálogo Liverpool")),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(builder: (context) => PantallaDetalle()),
            );
          },
          child: Text("Ver detalle del producto"),
        ),
      ),
    );
  }
}

// Pantalla de detalle
class PantallaDetalle extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Detalle del producto")),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.pop(context);
          },
          child: Text("Regresar"),
        ),
      ),
    );
  }
}

Al presionar "Ver detalle del producto", Flutter apila PantallaDetalle encima de PantallaInicio. Al presionar "Regresar", Navigator.pop elimina PantallaDetalle de la pila.


Ejemplo 2: Pasar datos entre pantallas

En apps reales necesitas enviar información a la siguiente pantalla. Por ejemplo, el nombre y precio de un producto de Mercado Libre.

Puedes hacerlo pasando argumentos al constructor del widget destino.

// Pantalla principal con datos
class PantallaInicio extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Mercado Libre MX")),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) => PantallaDetalle(
                  nombreProducto: "Licuadora Oster",
                  precio: 1200,
                ),
              ),
            );
          },
          child: Text("Ver Licuadora Oster"),
        ),
      ),
    );
  }
}

// Pantalla de detalle que recibe datos
class PantallaDetalle extends StatelessWidget {
  final String nombreProducto;
  final double precio;

  PantallaDetalle({required this.nombreProducto, required this.precio});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(nombreProducto)),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text("Producto: $nombreProducto"),
            Text("Precio: \$$precio"),
            ElevatedButton(
              onPressed: () => Navigator.pop(context),
              child: Text("Regresar"),
            ),
          ],
        ),
      ),
    );
  }
}

La pantalla de detalle mostraría: Precio: $1,200. Los datos viajan desde la pantalla principal hasta la de detalle de forma directa y segura.


Rutas con nombre

Las rutas con nombre son una forma de registrar todas tus pantallas en un solo lugar y navegar a ellas usando un identificador de texto.

Esto es útil cuando tu app tiene muchas pantallas, como una app de FEMSA con sección de productos, pedidos, perfil y soporte.

Cómo configurar rutas con nombre

Defines las rutas en el MaterialApp usando el parámetro routes.

void main() {
  runApp(MaterialApp(
    initialRoute: "/",
    routes: {
      "/": (context) => PantallaInicio(),
      "/detalle": (context) => PantallaDetalle(),
      "/perfil": (context) => PantallaPerfil(),
    },
  ));
}

Cada llave del mapa es el nombre de la ruta. El valor es el widget que se muestra.

Cómo navegar con rutas con nombre

Usas Navigator.pushNamed en lugar de Navigator.push.

Navigator.pushNamed(context, "/detalle");

Para regresar, Navigator.pop sigue funcionando igual.

Navigator.pop(context);

Ejemplo 3: App completa con rutas con nombre

Supón que estás construyendo una app sencilla inspirada en Bimbo para ver sus líneas de productos.

void main() {
  runApp(MaterialApp(
    title: "App Bimbo",
    initialRoute: "/",
    routes: {
      "/": (context) => PantallaInicio(),
      "/productos": (context) => PantallaProductos(),
      "/contacto": (context) => PantallaContacto(),
    },
  ));
}

class PantallaInicio extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Bimbo México")),
      body: Column(
        children: [
          ElevatedButton(
            onPressed: () => Navigator.pushNamed(context, "/productos"),
            child: Text("Ver productos"),
          ),
          ElevatedButton(
            onPressed: () => Navigator.pushNamed(context, "/contacto"),
            child: Text("Contacto"),
          ),
        ],
      ),
    );
  }
}

class PantallaProductos extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Productos Bimbo")),
      body: Center(child: Text("Pan Blanco, Marinela, Ricolino")),
    );
  }
}

class PantallaContacto extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Contacto")),
      body: Center(child: Text("Tel: 800-BIM-BIMBO")),
    );
  }
}

Desde PantallaInicio puedes ir a cualquier sección con una sola línea de código. La AppBar de Flutter agrega automáticamente el botón de regreso en Android e iOS.


Tabla comparativa: push vs pushNamed

Característica Navigator.push Navigator.pushNamed
Define la pantalla En el momento de navegar En MaterialApp al inicio
Pasar datos Por constructor del widget Por arguments o constructor
Organización Funciona bien en apps pequeñas Mejor para apps con muchas pantallas
Legibilidad Media Alta
Refactorización Más difícil Más fácil

Errores comunes

Error 1: Olvidar registrar la ruta en MaterialApp

Si usas Navigator.pushNamed(context, "/detalle") pero no declaras "/detalle" en routes, Flutter lanza un error en tiempo de ejecución. Siempre verifica que cada ruta nombrada esté registrada.

Error 2: Usar Navigator.pop en la pantalla raíz

Si haces pop en la primera pantalla de la app (la que está en la base de la pila), Flutter no tiene a dónde regresar. Esto puede cerrar la app en algunos dispositivos. Usa Navigator.canPop(context) para verificar antes de hacer pop.

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

Error 3: Pasar datos complejos por pushNamed sin usar arguments

Cuando usas rutas con nombre y necesitas enviar datos, usa el parámetro arguments.

Navigator.pushNamed(context, "/detalle", arguments: "Licuadora Oster");

En la pantalla destino los recuperas con:

final String producto = ModalRoute.of(context)!.settings.arguments as String;

No intentes pasar datos por variables globales; eso genera bugs difíciles de rastrear.

Error 4: Anidar demasiadas pantallas sin liberar la pila

Cada push agrega una pantalla a la pila. Si el usuario navega muchas veces sin hacer pop, la pila crece y consume más memoria. Para casos donde no necesitas regresar (por ejemplo, después de un login exitoso), usa Navigator.pushReplacement para reemplazar la pantalla actual sin acumular.

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

Resumen rápido de métodos Navigator

Método ¿Qué hace?
Navigator.push Agrega una pantalla a la pila
Navigator.pop Elimina la pantalla actual de la pila
Navigator.pushNamed Agrega una ruta con nombre a la pila
Navigator.pushReplacement Reemplaza la pantalla actual (sin acumular)
Navigator.popUntil Regresa hasta una ruta específica
Navigator.canPop Verifica si hay pantallas atrás en la pila

Dominar la navegación te permite construir apps con múltiples secciones, flujos de compra y formularios encadenados, exactamente lo que necesitan apps comerciales como las de Mercado Libre o Liverpool.

Puntos clave

  • `Navigator.push` apila una nueva pantalla encima de la actual; `Navigator.pop` la elimina y regresa a la anterior. Este modelo de pila es el núcleo de toda la navegación en Flutter.
  • Pasa datos entre pantallas usando el constructor del widget destino dentro de `MaterialPageRoute`. Es la forma más directa y segura en apps pequeñas.
  • Las rutas con nombre se registran en `MaterialApp` y permiten navegar usando identificadores de texto como `"/detalle"` o `"/perfil"`. Son ideales para apps con muchas pantallas.
  • Usa `Navigator.pushReplacement` cuando no necesites que el usuario regrese a la pantalla anterior, como después de un inicio de sesión exitoso.
  • Verifica con `Navigator.canPop(context)` antes de llamar `pop` para evitar errores al intentar regresar desde la pantalla raíz de la app.

Comparte esta lección:

¿Cómo navegar entre pantallas en Flutter? | Flutter y Dart Básico: Crea tu primera app móvil | Certmundo