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.