Flutter 导航与路由全面指南
一、基础导航概念
1. 导航与路由的区别
- 导航:指页面之间的跳转行为
- 路由:页面跳转的路径或规则
2. 核心组件
dart
// 基础导航组件
Navigator
MaterialPageRoute
RouteSettings
二、基础导航实现
1. 基本页面跳转
dart
// 跳转到新页面
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondPage(),
),
);
// 返回上一页
Navigator.pop(context);
// 带返回值的导航
class FirstPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ElevatedButton(
child: Text('跳转并获取结果'),
onPressed: () async {
// 等待新页面返回结果
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondPage(),
),
);
print('收到返回结果: $result');
},
),
),
);
}
}
class SecondPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ElevatedButton(
child: Text('返回数据'),
onPressed: () {
// 返回数据给上一个页面
Navigator.pop(context, '这是返回的数据');
},
),
),
);
}
}
2. 多种导航方法
dart
// 1. 替换当前页面(不能返回)
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => NewPage(),
),
);
// 2. 跳转到新页面并移除之前所有页面
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) => NewPage(),
),
(route) => false, // 返回false表示移除所有之前的页面
);
// 3. 跳转到新页面并保留特定页面
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) => NewPage(),
),
ModalRoute.withName('/home'), // 保留名为/home的页面
);
// 4. 弹出到指定页面
Navigator.popUntil(
context,
ModalRoute.withName('/home'),
);
三、命名路由
1. 基本配置
dart
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '命名路由示例',
// 方式1:使用 routes 定义路由表
routes: {
'/': (context) => HomePage(),
'/detail': (context) => DetailPage(),
'/settings': (context) => SettingsPage(),
'/profile': (context) => ProfilePage(),
},
// 方式2:使用 initialRoute 和 routes
initialRoute: '/',
// 方式3:处理未知路由
onUnknownRoute: (settings) {
return MaterialPageRoute(
builder: (context) => NotFoundPage(),
);
},
);
}
}
2. 使用命名路由
dart
// 跳转到命名路由
Navigator.pushNamed(context, '/detail');
// 替换命名路由
Navigator.pushReplacementNamed(context, '/detail');
// 跳转到命名路由并移除之前所有
Navigator.pushNamedAndRemoveUntil(
context,
'/detail',
(route) => false,
);
// 带参数的命名路由跳转
Navigator.pushNamed(
context,
'/detail',
arguments: {
'id': 123,
'title': '详情页',
},
);
3. 获取路由参数
dart
class DetailPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
// 获取路由参数
final args = ModalRoute.of(context)!.settings.arguments as Map?;
final id = args?['id'] ?? 0;
final title = args?['title'] ?? '详情';
return Scaffold(
appBar: AppBar(title: Text(title)),
body: Center(child: Text('ID: $id')),
);
}
}
四、路由传参方式
1. 构造函数传参(推荐)
dart
// 定义接收参数的页面
class DetailPage extends StatelessWidget {
final int id;
final String title;
final User user;
DetailPage({
required this.id,
required this.title,
required this.user,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(title)),
body: Column(
children: [
Text('ID: $id'),
Text('用户名: ${user.name}'),
Text('邮箱: ${user.email}'),
],
),
);
}
}
// 使用
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailPage(
id: 123,
title: '用户详情',
user: User(name: '张三', email: 'zhangsan@example.com'),
),
),
);
2. 使用 onGenerateRoute(灵活传参)
dart
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
onGenerateRoute: (settings) {
// 处理所有路由
switch (settings.name) {
case '/':
return MaterialPageRoute(builder: (_) => HomePage());
case '/detail':
final args = settings.arguments as Map;
return MaterialPageRoute(
builder: (_) => DetailPage(
id: args['id'],
title: args['title'],
user: args['user'],
),
);
case '/product':
final args = settings.arguments as Map;
return MaterialPageRoute(
builder: (_) => ProductPage(
productId: args['productId'],
category: args['category'],
),
);
default:
return MaterialPageRoute(
builder: (_) => NotFoundPage(),
);
}
},
);
}
}
3. 使用路由参数传递复杂对象
dart
// 定义数据模型
class Product {
final String id;
final String name;
final double price;
Product({required this.id, required this.name, required this.price});
}
// 跳转时传递对象
Navigator.pushNamed(
context,
'/product',
arguments: {
'product': Product(
id: 'p001',
name: 'Flutter开发指南',
price: 99.9,
),
},
);
// 在目标页面接收
class ProductPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final args = ModalRoute.of(context)!.settings.arguments as Map;
final product = args['product'] as Product;
return Scaffold(
appBar: AppBar(title: Text(product.name)),
body: Column(
children: [
Text('商品ID: ${product.id}'),
Text('价格: ¥${product.price}'),
],
),
);
}
}
五、路由守卫
1. 认证检查
dart
class MyApp extends StatelessWidget {
final AuthService authService = AuthService();
@override
Widget build(BuildContext context) {
return MaterialApp(
onGenerateRoute: (settings) {
// 路由守卫:检查是否需要登录
if (_needAuth(settings.name!) && !authService.isLoggedIn) {
// 未登录,跳转到登录页
return MaterialPageRoute(
builder: (_) => LoginPage(
onLoginSuccess: () {
// 登录成功后跳转到目标页面
Navigator.pushNamedAndRemoveUntil(
context,
settings.name!,
(route) => false,
);
},
),
);
}
// 正常路由处理
return _buildRoute(settings);
},
);
}
bool _needAuth(String routeName) {
// 需要认证的路由列表
final authRoutes = ['/profile', '/settings', '/order'];
return authRoutes.contains(routeName);
}
Route _buildRoute(RouteSettings settings) {
switch (settings.name) {
case '/':
return MaterialPageRoute(builder: (_) => HomePage());
case '/login':
return MaterialPageRoute(builder: (_) => LoginPage());
// ... 其他路由
default:
return MaterialPageRoute(builder: (_) => NotFoundPage());
}
}
}
2. 导航观察者
dart
class AppNavigatorObserver extends NavigatorObserver {
@override
void didPush(Route route, Route? previousRoute) {
print('推入新路由: ${route.settings.name}');
print('前一路由: ${previousRoute?.settings.name}');
// 可以在这里进行页面统计
Analytics.trackPageView(route.settings.name ?? 'unknown');
}
@override
void didPop(Route route, Route? previousRoute) {
print('返回路由: ${route.settings.name}');
}
@override
void didReplace({Route? newRoute, Route? oldRoute}) {
print('替换路由: ${oldRoute?.settings.name} -> ${newRoute?.settings.name}');
}
}
// 使用
MaterialApp(
navigatorObservers: [AppNavigatorObserver()],
);
六、嵌套导航
1. 底部导航栏 + 嵌套导航器
dart
class MainPage extends StatefulWidget {
@override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
int _currentIndex = 0;
// 为每个标签页创建独立的导航器
final List<GlobalKey<NavigatorState>> _navigatorKeys = [
GlobalKey<NavigatorState>(),
GlobalKey<NavigatorState>(),
GlobalKey<NavigatorState>(),
];
// 每个标签页的页面栈
final List<Widget> _pages = [
HomePage(),
SearchPage(),
ProfilePage(),
];
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
// 处理系统返回键
final currentNavigator = _navigatorKeys[_currentIndex];
if (currentNavigator.currentState!.canPop()) {
// 当前标签页有页面可返回
currentNavigator.currentState!.pop();
return false;
}
// 当前标签页没有页面可返回,退出应用
return true;
},
child: Scaffold(
body: IndexedStack(
index: _currentIndex,
children: _pages.asMap().entries.map((entry) {
final index = entry.key;
final page = entry.value;
return Navigator(
key: _navigatorKeys[index],
onGenerateRoute: (settings) {
return MaterialPageRoute(
builder: (context) => page,
);
},
);
}).toList(),
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: '首页',
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
label: '搜索',
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: '我的',
),
],
onTap: (index) {
if (_currentIndex == index) {
// 点击当前选中的标签,返回首页
_navigatorKeys[index].currentState?.popUntil(
(route) => route.isFirst,
);
} else {
setState(() {
_currentIndex = index;
});
}
},
),
),
);
}
}
2. Drawer 侧边栏导航
dart
class DrawerExample extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('侧边栏导航')),
drawer: Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: [
DrawerHeader(
decoration: BoxDecoration(
color: Theme.of(context).primaryColor,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CircleAvatar(
radius: 30,
backgroundImage: NetworkImage('https://example.com/avatar.jpg'),
),
SizedBox(height: 10),
Text(
'张三',
style: TextStyle(
color: Colors.white,
fontSize: 18,
),
),
Text(
'zhangsan@example.com',
style: TextStyle(
color: Colors.white70,
),
),
],
),
),
ListTile(
leading: Icon(Icons.home),
title: Text('首页'),
onTap: () {
Navigator.pop(context);
Navigator.pushNamed(context, '/');
},
),
ListTile(
leading: Icon(Icons.settings),
title: Text('设置'),
onTap: () {
Navigator.pop(context);
Navigator.pushNamed(context, '/settings');
},
),
ListTile(
leading: Icon(Icons.help),
title: Text('帮助'),
onTap: () {
Navigator.pop(context);
Navigator.pushNamed(context, '/help');
},
),
Divider(),
ListTile(
leading: Icon(Icons.exit_to_app),
title: Text('退出登录'),
onTap: () {
Navigator.pop(context);
_showLogoutDialog(context);
},
),
],
),
),
body: Center(child: Text('主内容区域')),
);
}
void _showLogoutDialog(BuildContext context) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('确认退出?'),
content: Text('确定要退出当前账号吗?'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('取消'),
),
TextButton(
onPressed: () {
// 执行退出逻辑
Navigator.pop(context);
Navigator.pushNamedAndRemoveUntil(
context,
'/login',
(route) => false,
);
},
child: Text('确定'),
),
],
),
);
}
}
七、自定义路由动画
1. 基础自定义动画
dart
// 渐变过渡
class FadeRoute extends PageRouteBuilder {
final Widget page;
FadeRoute({required this.page})
: super(
pageBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) => page,
transitionsBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) => FadeTransition(
opacity: animation,
child: child,
),
transitionDuration: Duration(milliseconds: 300),
);
}
// 使用
Navigator.push(
context,
FadeRoute(page: DetailPage()),
);
2. 滑动过渡
dart
// 从右侧滑入
class SlideRightRoute extends PageRouteBuilder {
final Widget page;
SlideRightRoute({required this.page})
: super(
pageBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) => page,
transitionsBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) {
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(1, 0),
end: Offset.zero,
).animate(animation),
child: child,
);
},
transitionDuration: Duration(milliseconds: 300),
);
}
// 从底部滑入
class SlideUpRoute extends PageRouteBuilder {
final Widget page;
SlideUpRoute({required this.page})
: super(
pageBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) => page,
transitionsBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) {
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(0, 1),
end: Offset.zero,
).animate(animation),
child: child,
);
},
transitionDuration: Duration(milliseconds: 300),
);
}
3. 缩放过渡
dart
class ScaleRoute extends PageRouteBuilder {
final Widget page;
ScaleRoute({required this.page})
: super(
pageBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) => page,
transitionsBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) {
return ScaleTransition(
scale: Tween<double>(
begin: 0.0,
end: 1.0,
).animate(
CurvedAnimation(
parent: animation,
curve: Curves.fastOutSlowIn,
),
),
child: child,
);
},
transitionDuration: Duration(milliseconds: 300),
);
}
4. 旋转过渡
dart
class RotationRoute extends PageRouteBuilder {
final Widget page;
RotationRoute({required this.page})
: super(
pageBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) => page,
transitionsBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) {
return RotationTransition(
turns: Tween<double>(
begin: 0.0,
end: 1.0,
).animate(
CurvedAnimation(
parent: animation,
curve: Curves.linear,
),
),
child: child,
);
},
transitionDuration: Duration(milliseconds: 500),
);
}
5. 组合动画
dart
class CombinedRoute extends PageRouteBuilder {
final Widget page;
CombinedRoute({required this.page})
: super(
pageBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) => page,
transitionsBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) {
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(1, 0),
end: Offset.zero,
).animate(animation),
child: FadeTransition(
opacity: animation,
child: ScaleTransition(
scale: Tween<double>(
begin: 0.5,
end: 1.0,
).animate(
CurvedAnimation(
parent: animation,
curve: Curves.easeOut,
),
),
child: child,
),
),
);
},
transitionDuration: Duration(milliseconds: 400),
);
}
八、Hero 动画
1. 基础 Hero 动画
dart
// 第一个页面
class FirstPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondPage(),
),
);
},
child: Hero(
tag: 'imageHero',
child: Container(
width: 100,
height: 100,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
image: DecorationImage(
image: NetworkImage('https://example.com/image.jpg'),
fit: BoxFit.cover,
),
),
),
),
),
),
);
}
}
// 第二个页面
class SecondPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('详情')),
body: Center(
child: Hero(
tag: 'imageHero',
child: Container(
width: 300,
height: 300,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
image: DecorationImage(
image: NetworkImage('https://example.com/image.jpg'),
fit: BoxFit.cover,
),
),
),
),
),
);
}
}
2. 多个 Hero 动画
dart
// 列表项
class ProductItem extends StatelessWidget {
final Product product;
ProductItem({required this.product});
@override
Widget build(BuildContext context) {
return ListTile(
leading: Hero(
tag: 'image_${product.id}',
child: CircleAvatar(
backgroundImage: NetworkImage(product.imageUrl),
),
),
title: Hero(
tag: 'title_${product.id}',
child: Material(
color: Colors.transparent,
child: Text(
product.name,
style: TextStyle(fontSize: 16),
),
),
),
subtitle: Text('¥${product.price}'),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProductDetailPage(product: product),
),
);
},
);
}
}
// 详情页
class ProductDetailPage extends StatelessWidget {
final Product product;
ProductDetailPage({required this.product});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('商品详情')),
body: Column(
children: [
Hero(
tag: 'image_${product.id}',
child: Image.network(
product.imageUrl,
width: double.infinity,
height: 300,
fit: BoxFit.cover,
),
),
Padding(
padding: EdgeInsets.all(16),
child: Hero(
tag: 'title_${product.id}',
child: Material(
color: Colors.transparent,
child: Text(
product.name,
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
),
),
),
],
),
);
}
}
九、路由管理最佳实践
1. 路由集中管理
dart
// routes.dart
class AppRoutes {
// 路由名称常量
static const String home = '/';
static const String login = '/login';
static const String register = '/register';
static const String profile = '/profile';
static const String settings = '/settings';
static const String detail = '/detail';
static const String notFound = '/404';
// 路由生成器
static Route<dynamic> generateRoute(RouteSettings settings) {
switch (settings.name) {
case home:
return MaterialPageRoute(builder: (_) => HomePage());
case login:
return MaterialPageRoute(builder: (_) => LoginPage());
case register:
return MaterialPageRoute(builder: (_) => RegisterPage());
case profile:
final args = settings.arguments as Map?;
return MaterialPageRoute(
builder: (_) => ProfilePage(
userId: args?['userId'] ?? '',
),
);
case detail:
final args = settings.arguments as Map;
return MaterialPageRoute(
builder: (_) => DetailPage(
id: args['id'],
title: args['title'],
),
);
default:
return MaterialPageRoute(builder: (_) => NotFoundPage());
}
}
}
// main.dart
void main() {
runApp(
MaterialApp(
title: '我的应用',
theme: ThemeData(primarySwatch: Colors.blue),
initialRoute: AppRoutes.home,
onGenerateRoute: AppRoutes.generateRoute,
onUnknownRoute: (settings) {
return MaterialPageRoute(
builder: (_) => NotFoundPage(),
);
},
),
);
}
2. 导航服务
dart
class NavigationService {
static GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
// 跳转到命名路由
static Future<dynamic> navigateTo(String routeName, {dynamic arguments}) {
return navigatorKey.currentState!.pushNamed(
routeName,
arguments: arguments,
);
}
// 替换当前路由
static Future<dynamic> replaceWith(String routeName, {dynamic arguments}) {
return navigatorKey.currentState!.pushReplacementNamed(
routeName,
arguments: arguments,
);
}
// 跳转并清除所有历史
static Future<dynamic> navigateAndRemoveUntil(
String routeName,
bool Function(Route<dynamic>) predicate, {
dynamic arguments,
}) {
return navigatorKey.currentState!.pushNamedAndRemoveUntil(
routeName,
predicate,
arguments: arguments,
);
}
// 返回
static void goBack([dynamic result]) {
return navigatorKey.currentState!.pop(result);
}
// 检查是否可以返回
static bool canGoBack() {
return navigatorKey.currentState!.canPop();
}
}
// 使用
// 在任何地方都可以调用导航
NavigationService.navigateTo('/detail', arguments: {'id': 123});
3. 路由守卫服务
dart
class RouteGuard {
final AuthService authService;
RouteGuard({required this.authService});
// 检查路由是否需要认证
bool needAuth(String routeName) {
final protectedRoutes = [
'/profile',
'/settings',
'/order',
'/cart',
];
return protectedRoutes.contains(routeName);
}
// 检查用户权限
bool hasPermission(String routeName) {
final userRole = authService.currentUser?.role;
switch (routeName) {
case '/admin':
return userRole == 'admin';
case '/manager':
return userRole == 'admin' || userRole == 'manager';
default:
return true;
}
}
// 获取重定向路由
String? getRedirectRoute(String routeName) {
if (needAuth(routeName) && !authService.isLoggedIn) {
return '/login';
}
if (!hasPermission(routeName)) {
return '/unauthorized';
}
return null;
}
}
十、第三方路由库
1. Go Router(官方推荐)
yaml
dependencies:
go_router: ^6.0.0
dart
import 'package:go_router/go_router.dart';
final GoRouter router = GoRouter(
routes: [
GoRoute(
path: '/',
builder: (context, state) => HomePage(),
routes: [
GoRoute(
path: 'detail/:id',
builder: (context, state) {
final id = state.pathParameters['id'];
return DetailPage(id: id!);
},
),
GoRoute(
path: 'profile',
builder: (context, state) => ProfilePage(),
),
],
),
GoRoute(
path: '/login',
builder: (context, state) => LoginPage(),
),
],
// 路由守卫
redirect: (context, state) {
final isLoggedIn = false; // 从状态管理获取
final isLoggingIn = state.matchedLocation == '/login';
if (!isLoggedIn && !isLoggingIn) {
return '/login';
}
if (isLoggedIn && isLoggingIn) {
return '/';
}
return null;
},
// 错误页面
errorBuilder: (context, state) => NotFoundPage(),
);
// 在 MaterialApp 中使用
MaterialApp.router(
routerConfig: router,
);
// 使用
context.go('/detail/123');// 跳转
context.pop();// 返回
2. Auto Route
yaml
dependencies:
auto_route: ^4.0.0
dev_dependencies:
auto_route_generator: ^4.0.0
build_runner: ^2.0.0
dart
// 定义路由
@MaterialAutoRouter(
replaceInRouteName: 'Page,Route',
routes: [
AutoRoute(page: HomePage, initial: true),
AutoRoute(page: DetailPage),
AutoRoute(page: ProfilePage),
],
)
class $AppRouter {}
// 生成路由文件
// flutter pub run build_runner build
// 使用
final router = AppRouter();
MaterialApp.router(
routerDelegate: router.delegate(),
routeInformationParser: router.defaultRouteParser(),
);
十一、调试技巧
1. 查看路由栈
dart
// 打印当前路由栈
void printRouteStack(BuildContext context) {
Navigator.of(context).widget.pages.forEach((page) {
print('Route: ${page.name}');
});
}
// 或者使用
Navigator.of(context).toStringDeep();
2. 路由调试组件
dart
class RouteDebugWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return FloatingActionButton(
onPressed: () {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('路由栈信息'),
content: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('当前路由: ${ModalRoute.of(context)?.settings.name}'),
Divider(),
Text('路由栈:'),
...Navigator.of(context).widget.pages.map((page) {
return Text('- ${page.name ?? '未命名'}');
}).toList(),
],
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('关闭'),
),
],
),
);
},
child: Icon(Icons.route),
);
}
}
总结
Flutter 导航与路由系统非常强大,提供了多种方式来管理页面跳转:
- 基础导航 :适合简单应用,使用
Navigator.push/pop - 命名路由:适合中小型应用,便于管理
- 路由守卫:实现权限控制和认证检查
- 嵌套导航:处理复杂的页面结构
- 自定义动画:提升用户体验
- 第三方库:大型应用推荐使用 Go Router 或 Auto Route
选择适合项目规模的方案,并保持路由逻辑的清晰和可维护性。对于大多数应用,推荐使用命名路由结合路由守卫的方式,既简单又灵活。