Navigation & Routing
How Flutter navigation works
Flutter manages screens as a stack: you push a new screen on top to go forward, and pop it to go back. The Navigator handles this stack, the transition animations, and the back button.
Pushing and popping
// go to a new screen
Navigator.push(
context,
MaterialPageRoute(builder: (_) => const ProfilePage()),
);
// go back
Navigator.pop(context);
Passing data forward
Pass data by giving it to the destination widget’s constructor:
Navigator.push(
context,
MaterialPageRoute(builder: (_) => ProfilePage(userId: 42)),
);
class ProfilePage extends StatelessWidget {
final int userId;
const ProfilePage({required this.userId, super.key});
// ...
}
Getting data back
push returns a Future that completes when the screen pops — perfect for “pick something and return it”:
final selected = await Navigator.push<String>(
context,
MaterialPageRoute(builder: (_) => const PickerPage()),
);
// in PickerPage: Navigator.pop(context, 'Apple');
Named routes
For larger apps, register routes by name to avoid building MaterialPageRoute everywhere:
MaterialApp(
routes: {
'/': (_) => const HomePage(),
'/settings': (_) => const SettingsPage(),
},
);
Navigator.pushNamed(context, '/settings');
go_router for modern, scalable navigation
For bigger apps, deep links and web support, the go_router package (officially recommended) gives clean, URL-based routing.
final router = GoRouter(routes: [
GoRoute(path: '/', builder: (_, __) => const HomePage()),
GoRoute(path: '/user/:id', builder: (_, s) => UserPage(id: s.pathParameters['id']!)),
]);
context.go('/user/42');
Bottom navigation
For tabbed apps, combine a Scaffold with a BottomNavigationBar (or NavigationBar in Material 3) and switch the body based on the selected index.
Common mistakes
- Forgetting to
awaitthe result ofpushwhen you need a return value. - Passing huge objects between screens instead of an id.
- Building a complex app on raw
Navigatorwhengo_routerwould be cleaner.
Summary: Flutter navigates with a stack —pushto go forward,popto go back. Pass data via constructors, get results from the returned Future, use named routes for structure, and adoptgo_routerfor larger, URL-driven apps.