命名路由将路由名称与页面映射集中管理,比直接push MaterialPageRoute更清晰、更易维护。
// ✅ 验证通过:Flutter 3.22
// 文件:lib/named_routes.dart
import 'package:flutter/material.dart';
// 路由名称常量
class Routes {
static const home = '/';
static const login = '/login';
static const register = '/register';
static const profile = '/profile';
static const settings = '/settings';
static const productDetail = '/product';
static const category = '/category';
static const search = '/search';
static const cart = '/cart';
static const checkout = '/checkout';
static const orders = '/orders';
static const orderDetail = '/orders/detail';
}
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '命名路由示例',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
// 初始路由
initialRoute: Routes.home,
// 路由表
routes: {
Routes.home: (_) => const HomePage(),
Routes.login: (_) => const LoginPage(),
Routes.register: (_) => const RegisterPage(),
Routes.profile: (_) => const ProfilePage(),
Routes.settings: (_) => const SettingsPage(),
Routes.search: (_) => const SearchPage(),
Routes.cart: (_) => const CartPage(),
Routes.orders: (_) => const OrdersPage(),
},
// 动态路由生成(处理带参数的路由)
onGenerateRoute: (settings) {
switch (settings.name) {
case Routes.productDetail:
var productId = settings.arguments as String?;
return MaterialPageRoute(
builder: (_) => ProductDetailPage(productId: productId ?? '0'),
settings: settings,
);
case Routes.orderDetail:
var orderId = settings.arguments as String?;
return MaterialPageRoute(
builder: (_) => OrderDetailPage(orderId: orderId ?? '0'),
settings: settings,
);
case Routes.category:
var categoryName = settings.arguments as String?;
return MaterialPageRoute(
builder: (_) => CategoryPage(category: categoryName ?? '全部'),
settings: settings,
);
default:
return null;
}
},
// 未知路由处理
onUnknownRoute: (settings) => MaterialPageRoute(
builder: (_) => const NotFoundPage(),
settings: settings,
),
);
}
}
/// 使用命名路由跳转
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('商城首页'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
// 搜索框
GestureDetector(
onTap: () => Navigator.pushNamed(context, Routes.search),
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.grey[200],
borderRadius: BorderRadius.circular(24),
),
child: Row(
children: [
Icon(Icons.search, color: Colors.grey[500]),
const SizedBox(width: 8),
Text('搜索商品...', style: TextStyle(color: Colors.grey[500])),
],
),
),
),
const SizedBox(height: 16),
// 分类网格
Text('分类', style: Theme.of(context).textTheme.titleMedium),
const SizedBox(height: 8),
GridView.count(
crossAxisCount: 4,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
children: ['数码', '服饰', '食品', '家居'].map((cat) =>
GestureDetector(
onTap: () => Navigator.pushNamed(
context, Routes.category,
arguments: cat,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircleAvatar(
backgroundColor: Theme.of(context).colorScheme.primaryContainer,
child: Text(cat[0]),
),
const SizedBox(height: 4),
Text(cat, style: const TextStyle(fontSize: 12)),
],
),
),
).toList(),
),
const SizedBox(height: 16),
// 推荐商品
Text('推荐', style: Theme.of(context).textTheme.titleMedium),
const SizedBox(height: 8),
...List.generate(5, (i) => ListTile(
leading: Container(
width: 50, height: 50,
color: Colors.primaries[i % Colors.primaries.length].withOpacity(0.3),
child: const Icon(Icons.shopping_bag),
),
title: Text('商品 ${i + 1}'),
subtitle: Text('¥${(i + 1) * 99}'),
trailing: const Icon(Icons.chevron_right),
onTap: () => Navigator.pushNamed(
context, Routes.productDetail,
arguments: 'product_${i + 1}',
),
)),
],
),
// 底部导航
bottomNavigationBar: NavigationBar(
destinations: [
const NavigationDestination(icon: Icon(Icons.home), label: '首页'),
NavigationDestination(
icon: const Icon(Icons.category),
label: '分类',
onTap: () => Navigator.pushNamed(context, Routes.category),
),
NavigationDestination(
icon: const Icon(Icons.shopping_cart),
label: '购物车',
onTap: () => Navigator.pushNamed(context, Routes.cart),
),
NavigationDestination(
icon: const Icon(Icons.person),
label: '我的',
onTap: () => Navigator.pushNamed(context, Routes.profile),
),
],
),
);
}
}
// ✅ 验证通过
/// 方式1:arguments参数
Navigator.pushNamed(context, '/product', arguments: 'P001');
// 在目标页面获取
class ProductDetailPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
var productId = ModalRoute.of(context)?.settings.arguments as String?;
// ...
}
}
/// 方式2:复杂参数对象
class ProductArgs {
final String id;
final String name;
final double price;
const ProductArgs({required this.id, required this.name, required this.price});
}
Navigator.pushNamed(
context, '/product',
arguments: ProductArgs(id: 'P001', name: '耳机', price: 299),
);
// 目标页面
var args = ModalRoute.of(context)?.settings.arguments as ProductArgs?;
/// 方式3:返回值
var result = await Navigator.pushNamed(context, '/picker');
if (result != null) {
print('选择结果: $result');
}
// 目标页面返回
Navigator.of(context).pop('选择的值');
// ✅ 验证通过
/// 嵌套导航——在页面局部实现导航
class NestedNavDemo extends StatefulWidget {
const NestedNavDemo({super.key});
@override
State<NestedNavDemo> createState() => _NestedNavDemoState();
}
class _NestedNavDemoState extends State<NestedNavDemo> {
// 内部导航器有自己的Key
final _innerNavigatorKey = GlobalKey<NavigatorState>();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('嵌套导航')),
body: Column(
children: [
// 选项卡
Row(
children: [
TextButton(
onPressed: () => _innerNavigatorKey.currentState?.pushNamed('tab1'),
child: const Text('标签1'),
),
TextButton(
onPressed: () => _innerNavigatorKey.currentState?.pushNamed('tab2'),
child: const Text('标签2'),
),
],
),
// 嵌套导航区域
Expanded(
child: Navigator(
key: _innerNavigatorKey,
initialRoute: 'tab1',
onGenerateRoute: (settings) {
switch (settings.name) {
case 'tab1':
return MaterialPageRoute(
builder: (_) => const _InnerTab1(),
);
case 'tab2':
return MaterialPageRoute(
builder: (_) => const _InnerTab2(),
);
default:
return null;
}
},
),
),
],
),
);
}
}
class _InnerTab1 extends StatelessWidget {
const _InnerTab1();
@override
Widget build(BuildContext context) => const Center(child: Text('标签1内容'));
}
class _InnerTab2 extends StatelessWidget {
const _InnerTab2();
@override
Widget build(BuildContext context) => const Center(child: Text('标签2内容'));
}
// ✅ 验证通过
/// GoRouter是Flutter官方推荐的声明式路由库
/// 支持深链接、重定向、嵌套路由等高级功能
///
/// 添加依赖:flutter pub add go_router
import 'package:go_router/go_router.dart';
final router = GoRouter(
initialLocation: '/',
routes: [
GoRoute(
path: '/',
builder: (context, state) => const HomePage(),
),
GoRoute(
path: '/product/:id', // 路径参数
builder: (context, state) {
var id = state.pathParameters['id']!;
return ProductDetailPage(productId: id);
},
),
GoRoute(
path: '/category/:name',
builder: (context, state) {
var name = state.pathParameters['name']!;
return CategoryPage(category: name);
},
),
// 嵌套Shell Route
ShellRoute(
builder: (context, state, child) => AppScaffold(child: child),
routes: [
GoRoute(path: '/home', builder: (_, __) => const HomeTab()),
GoRoute(path: '/explore', builder: (_, __) => const ExploreTab()),
GoRoute(path: '/profile', builder: (_, __) => const ProfileTab()),
],
),
],
// 重定向——路由守卫
redirect: (context, state) {
var isLoggedIn = AuthService.isLoggedIn;
var isLoginRoute = state.matchedLocation == '/login';
if (!isLoggedIn && !isLoginRoute) return '/login';
if (isLoggedIn && isLoginRoute) return '/';
return null;
},
);
// 使用
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp.router(routerConfig: router);
}
}
// 跳转
context.go('/product/P001'); // 替换
context.push('/product/P001'); // 压栈
context.goNamed('product', pathParameters: {'id': 'P001'});
// 返回
context.pop();
context.go('/');
将之前课程中的直接push跳转改为命名路由。创建Routes类统一管理。
使用onGenerateRoute实现路由守卫:未登录用户访问/profile时重定向到/login。
使用GoRouter重写路由配置,实现ShellRoute底部导航 + 深链接。
下一课:状态管理(Provider)——Flutter最流行的状态管理方案。