Flutter Navigation and Routing
This section will introduce the navigation and routing mechanisms in Flutter, including page transitions, parameter passing, and named routes.
Navigator Basics
Flutter uses Navigator for page management, implementing page transitions based on a stack structure.
Example: Basic Page Navigation
// Navigate to a new page
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const DetailPage(),
),
);
// Return to the previous page
Navigator.of(context).pop();
// Return with result
Navigator.of(context).pop('Returned data');
// Replace current page (cannot go back)
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (context) => const HomePage(),
),
);
// Return to root page
Navigator.of(context).popUntil((route) => route.isFirst);
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const DetailPage(),
),
);
// Return to the previous page
Navigator.of(context).pop();
// Return with result
Navigator.of(context).pop('Returned data');
// Replace current page (cannot go back)
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (context) => const HomePage(),
),
);
// Return to root page
Navigator.of(context).popUntil((route) => route.isFirst);
MaterialPageRoute vs CupertinoPageRoute
Example: Page Transition Animations on Different Platforms
// Android style - rises from the bottom
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const DetailPage(),
),
);
// iOS style - slide in from the right
Navigator.of(context).push(
CupertinoPageRoute(
builder: (context) => const DetailPage(),
),
);
// Custom transition effect
Navigator.of(context).push(
PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) {
return const DetailPage();
},
transitionsBuilder: (context, animation, secondaryAnimation, child) {
// Fade in/out effect
return FadeTransition(
opacity: animation,
child: child,
);
},
transitionDuration: const Duration(milliseconds: 300),
),
);
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const DetailPage(),
),
);
// iOS style - slide in from the right
Navigator.of(context).push(
CupertinoPageRoute(
builder: (context) => const DetailPage(),
),
);
// Custom transition effect
Navigator.of(context).push(
PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) {
return const DetailPage();
},
transitionsBuilder: (context, animation, secondaryAnimation, child) {
// Fade in/out effect
return FadeTransition(
opacity: animation,
child: child,
);
},
transitionDuration: const Duration(milliseconds: 300),
),
);
Page Parameter Passing
< h2 class="example">Example: Passing parameters to a new page
// Jump to detail page and pass parameters
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => ProductDetailPage(productId: 123),
),
);
// Detail page receives parameters
class ProductDetailPage extends StatelessWidget {
final int productId;
const ProductDetailPage({
super.key,
required this.productId,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Product $productId')),
body: Center(child: Text('Product ID: $productId')),
);
}
}
// Pass multiple parameters
class UserProfilePage extends StatelessWidget {
final String name;
final int age;
const UserProfilePage({
super.key,
required this.name,
required this.age,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text($name's homepage)),
body: Center(child: Text('Age: $age')),
);
}
}
// Jump
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const UserProfilePage(
name: 'Zhang San',
age: 25,
),
),
);
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => ProductDetailPage(productId: 123),
),
);
// Detail page receives parameters
class ProductDetailPage extends StatelessWidget {
final int productId;
const ProductDetailPage({
super.key,
required this.productId,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Product $productId')),
body: Center(child: Text('Product ID: $productId')),
);
}
}
// Pass multiple parameters
class UserProfilePage extends StatelessWidget {
final String name;
final int age;
const UserProfilePage({
super.key,
required this.name,
required this.age,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text($name's homepage)),
body: Center(child: Text('Age: $age')),
);
}
}
// Jump
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const UserProfilePage(
name: 'Zhang San',
age: 25,
),
),
);
Named route
Using named routes can simplify page transition configuration.
Example: Named Route Configuration
void main() {
runApp(
MaterialApp(
// Define routing table
initialRoute: '/',
routes: {
'/': (context) => const HomePage(),
'/detail': (context) => const DetailPage(),
'/user': (context) => const UserPage(),
'/settings': (context) => const SettingsPage(),
},
),
);
}
// Jump
Navigator.pushNamed(context, '/detail');
// Navigate with parameters
Navigator.pushNamed(context, '/user', arguments: {'name': 'Zhang San', 'age': 25});
// Get parameters on the target page
class DetailPage extends StatelessWidget {
const DetailPage({super.key});
@override
Widget build(BuildContext context) {
// Get the passed parameters
final args = ModalRoute.of(context)?.settings.arguments as Map?;
return Scaffold(
appBar: AppBar(title: const Text('Detail Page')),
body: Center(
child: Text(Arguments: $args),
),
);
}
}
runApp(
MaterialApp(
// Define routing table
initialRoute: '/',
routes: {
'/': (context) => const HomePage(),
'/detail': (context) => const DetailPage(),
'/user': (context) => const UserPage(),
'/settings': (context) => const SettingsPage(),
},
),
);
}
// Jump
Navigator.pushNamed(context, '/detail');
// Navigate with parameters
Navigator.pushNamed(context, '/user', arguments: {'name': 'Zhang San', 'age': 25});
// Get parameters on the target page
class DetailPage extends StatelessWidget {
const DetailPage({super.key});
@override
Widget build(BuildContext context) {
// Get the passed parameters
final args = ModalRoute.of(context)?.settings.arguments as Map?;
return Scaffold(
appBar: AppBar(title: const Text('Detail Page')),
body: Center(
child: Text(Arguments: $args),
),
);
}
}
Another Way to Pass Values in Routing
Example: onGenerateRoute
MaterialApp(
onGenerateRoute: (settings) {
// Return different pages based on the route name
switch (settings.name) {
case '/':
return MaterialPageRoute(
builder: (_) => const HomePage(),
);
case '/detail':
// Retrieve parameters from arguments
final args = settings.arguments as Map<String, dynamic>?;
return MaterialPageRoute(
builder: (_) => DetailPage(
id: args?['id'] ?? 0,
title: args?['title'] ?? '',
),
);
case '/product':
final productId = settings.name?.split('/').last;
return MaterialPageRoute(
builder: (_) => ProductPage(id: productId ?? ''),
);
default:
return MaterialPageRoute(
builder: (_) => const NotFoundPage(),
);
}
},
)
onGenerateRoute: (settings) {
// Return different pages based on the route name
switch (settings.name) {
case '/':
return MaterialPageRoute(
builder: (_) => const HomePage(),
);
case '/detail':
// Retrieve parameters from arguments
final args = settings.arguments as Map<String, dynamic>?;
return MaterialPageRoute(
builder: (_) => DetailPage(
id: args?['id'] ?? 0,
title: args?['title'] ?? '',
),
);
case '/product':
final productId = settings.name?.split('/').last;
return MaterialPageRoute(
builder: (_) => ProductPage(id: productId ?? ''),
);
default:
return MaterialPageRoute(
builder: (_) => const NotFoundPage(),
);
}
},
)
other extensionsFor simple applications, basic Navigator.push is sufficient; for medium to large applications, it is recommended to use named routes or routing management libraries to centrally manage pages.