Flutter provides two navigation systems: the classic Navigator and the newer GoRouter for declarative routing.
Navigator 1.0 (Classic)
Dart
// Push a new screen
Navigator.push(
context,
MaterialPageRoute(builder: (context) => DetailScreen(id: 42)),
);
// Push and wait for result
final result = await Navigator.push<String>(
context,
MaterialPageRoute(builder: (context) => SearchScreen()),
);
print('User selected: $result');
// Pop back
Navigator.pop(context);
// Pop with result
Navigator.pop(context, 'selected_item');
// Replace current screen (no back button)
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => HomeScreen()),
);
// Clear stack and go to a screen
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => LoginScreen()),
(route) => false, // Remove all routes
);Navigator 2.0 with GoRouter (Recommended)
YAML
dependencies:
go_router: ^13.0.0Dart
// lib/router.dart
import 'package:go_router/go_router.dart';
final router = GoRouter(
initialLocation: '/',
routes: [
GoRoute(
path: '/',
builder: (context, state) => const HomeScreen(),
),
GoRoute(
path: '/courses',
builder: (context, state) => const CourseListScreen(),
routes: [
GoRoute(
path: ':courseId',
builder: (context, state) => CourseDetailScreen(
courseId: state.pathParameters['courseId']!,
),
routes: [
GoRoute(
path: ':lessonId',
builder: (context, state) => LessonScreen(
courseId: state.pathParameters['courseId']!,
lessonId: state.pathParameters['lessonId']!,
),
),
],
),
],
),
GoRoute(
path: '/profile',
builder: (context, state) => const ProfileScreen(),
),
],
);Dart
// main.dart
import 'router.dart';
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp.router(
title: 'My App',
routerConfig: router,
);
}
}Navigation with GoRouter
Dart
// Simple navigation
context.go('/courses');
context.push('/courses/flutter/intro');
// Navigate with extra data
context.push('/profile', extra: UserProfile(name: 'Alice'));
// Replace current route
context.go('/courses');
// Go back
context.pop();
// Get current route info
final courseId = state.pathParameters['courseId'];
final queryParams = state.uri.queryParameters;Passing Data Between Screens
Dart
// Method 1: Constructor parameters
class DetailScreen extends StatelessWidget {
final String id;
final String title;
const DetailScreen({required this.id, required this.title});
@override
Widget build(BuildContext context) => Scaffold(body: Text(title));
}
// Push: DetailScreen(id: '1', title: 'My Item')
// Method 2: Route extra data
context.push('/detail', extra: {'id': '1', 'title': 'My Item'});
// Method 3: Named routes with parameters
// /courses/:courseId — state.pathParameters['courseId']Bottom Navigation
Dart
class MainScreen extends StatefulWidget {
const MainScreen({super.key});
@override
State<MainScreen> createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
int _currentIndex = 0;
final _screens = [
const HomeScreen(),
const SearchScreen(),
const ProfileScreen(),
];
@override
Widget build(BuildContext context) {
return Scaffold(
body: _screens[_currentIndex],
bottomNavigationBar: NavigationBar(
selectedIndex: _currentIndex,
onDestinationSelected: (index) => setState(() => _currentIndex = index),
destinations: const [
NavigationDestination(icon: Icon(Icons.home), label: 'Home'),
NavigationDestination(icon: Icon(Icons.search), label: 'Search'),
NavigationDestination(icon: Icon(Icons.person), label: 'Profile'),
],
),
);
}
}Page Transitions
Dart
// Custom transition
Navigator.push(
context,
PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) => DetailScreen(),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
final tween = Tween(begin: Offset(0, 1), end: Offset.zero)
.chain(CurveTween(curve: Curves.easeInOut));
return SlideTransition(
position: animation.drive(tween),
child: child,
);
},
),
);
// GoRouter custom transition
GoRoute(
path: '/detail',
pageBuilder: (context, state) => CustomTransitionPage(
child: DetailScreen(),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return FadeTransition(opacity: animation, child: child);
},
),
)💡 Tip: Use GoRouter for new projects — it handles deep linking, redirects, and nested routes cleanly.
Next: Networking and APIs