Flutter 路由导航完全指南
·
Flutter 路由导航完全指南
引言
路由导航是任何移动应用的核心功能之一。Flutter 提供了强大而灵活的路由导航系统,支持多种导航方式。本文将深入探讨 Flutter 路由导航的各种用法和高级技巧。
基础概念回顾
路由类型
- 命名路由: 通过名称导航
- 匿名路由: 直接使用 Navigator.push
- 嵌套路由: 在父路由中嵌套子路由
核心组件
- Navigator: 管理路由栈
- Route: 单个路由对象
- MaterialPageRoute: Material风格路由
- CupertinoPageRoute: iOS风格路由
高级技巧一:基础导航
基本导航
// 导航到新页面
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const SecondPage()),
);
// 导航并返回数据
final result = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => const SecondPage()),
);
// 弹出当前页面
Navigator.pop(context);
// 弹出并返回数据
Navigator.pop(context, 'result');
命名路由
// 注册路由
MaterialApp(
routes: {
'/': (context) => const HomePage(),
'/second': (context) => const SecondPage(),
},
);
// 使用命名路由导航
Navigator.pushNamed(context, '/second');
// 带参数的命名路由
Navigator.pushNamed(context, '/second', arguments: 'Hello');
// 获取参数
final args = ModalRoute.of(context)?.settings.arguments as String;
高级技巧二:路由管理
路由配置
class AppRouter {
static Route<dynamic> generateRoute(RouteSettings settings) {
switch (settings.name) {
case '/':
return MaterialPageRoute(builder: (_) => const HomePage());
case '/second':
return MaterialPageRoute(builder: (_) => const SecondPage());
case '/detail':
final id = settings.arguments as int;
return MaterialPageRoute(builder: (_) => DetailPage(id: id));
default:
return MaterialPageRoute(
builder: (_) => const NotFoundPage(),
);
}
}
}
// 使用
MaterialApp(
onGenerateRoute: AppRouter.generateRoute,
);
路由守卫
class AuthGuard extends StatelessWidget {
final Widget child;
const AuthGuard({super.key, required this.child});
@override
Widget build(BuildContext context) {
final isLoggedIn = AuthService.instance.isLoggedIn;
if (!isLoggedIn) {
WidgetsBinding.instance.addPostFrameCallback((_) {
Navigator.pushReplacementNamed(context, '/login');
});
return const SplashScreen();
}
return child;
}
}
// 使用
MaterialApp(
routes: {
'/home': (context) => const AuthGuard(child: HomePage()),
},
);
高级技巧三:页面过渡动画
自定义页面路由
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,
) =>
SlideTransition(
position: Tween<Offset>(
begin: const Offset(1.0, 0.0),
end: Offset.zero,
).animate(animation),
child: child,
),
);
}
// 使用
Navigator.push(context, SlideRightRoute(page: const SecondPage()));
淡入淡出动画
class FadeRoute extends PageRouteBuilder {
final Widget page;
FadeRoute({required this.page})
: super(
pageBuilder: (context, animation, secondaryAnimation) => page,
transitionsBuilder: (context, animation, secondaryAnimation, child) =>
FadeTransition(
opacity: animation,
child: child,
),
);
}
缩放动画
class ScaleRoute extends PageRouteBuilder {
final Widget page;
ScaleRoute({required this.page})
: super(
pageBuilder: (context, animation, secondaryAnimation) => page,
transitionsBuilder: (context, animation, secondaryAnimation, child) =>
ScaleTransition(
scale: Tween<double>(begin: 0.5, end: 1.0).animate(
CurvedAnimation(parent: animation, curve: Curves.easeOut),
),
child: child,
),
);
}
高级技巧四:嵌套导航
Tab导航
class TabNavigator extends StatelessWidget {
const TabNavigator({super.key, required this.navigatorKey});
final GlobalKey<NavigatorState> navigatorKey;
@override
Widget build(BuildContext context) {
return Navigator(
key: navigatorKey,
initialRoute: '/',
onGenerateRoute: (settings) {
return MaterialPageRoute(
builder: (context) => const TabPage(),
);
},
);
}
}
// 使用
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 3,
child: Scaffold(
body: TabBarView(
children: [
TabNavigator(navigatorKey: GlobalKey()),
TabNavigator(navigatorKey: GlobalKey()),
TabNavigator(navigatorKey: GlobalKey()),
],
),
bottomNavigationBar: const TabBar(
tabs: [
Tab(icon: Icon(Icons.home)),
Tab(icon: Icon(Icons.search)),
Tab(icon: Icon(Icons.person)),
],
),
),
);
}
}
实战案例:路由管理类
class AppNavigator {
static void goToHome(BuildContext context) {
Navigator.pushNamedAndRemoveUntil(
context,
'/home',
(route) => false,
);
}
static void goToLogin(BuildContext context) {
Navigator.pushNamed(context, '/login');
}
static Future<void> goToDetail(BuildContext context, int id) async {
await Navigator.pushNamed(
context,
'/detail',
arguments: id,
);
}
static void goBack(BuildContext context) {
Navigator.pop(context);
}
static void goBackWithResult(BuildContext context, dynamic result) {
Navigator.pop(context, result);
}
}
// 使用
AppNavigator.goToHome(context);
AppNavigator.goToDetail(context, 123);
实战案例:动态路由
class DynamicRoute extends StatelessWidget {
const DynamicRoute({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Dynamic Route')),
body: Center(
child: ElevatedButton(
onPressed: () {
// 动态生成路由
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const DynamicDetailPage(),
settings: const RouteSettings(
name: '/dynamic-detail',
arguments: {'id': 1, 'name': 'Dynamic'},
),
),
);
},
child: const Text('Go to Dynamic Detail'),
),
),
);
}
}
实战案例:路由监听
class RouteObserverService extends RouteObserver<PageRoute<dynamic>> {
@override
void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {
super.didPush(route, previousRoute);
if (route is PageRoute) {
print('Pushed: ${route.settings.name}');
AnalyticsService.logPageView(route.settings.name ?? '');
}
}
@override
void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) {
super.didPop(route, previousRoute);
if (route is PageRoute) {
print('Popped: ${route.settings.name}');
}
}
}
// 使用
MaterialApp(
navigatorObservers: [RouteObserverService()],
);
实战案例:Deep Link 处理
class DeepLinkHandler {
static void handleDeepLink(Uri uri) {
final path = uri.path;
final params = uri.queryParameters;
switch (path) {
case '/detail':
final id = int.parse(params['id'] ?? '0');
// 导航到详情页
break;
case '/search':
final query = params['q'] ?? '';
// 导航到搜索页
break;
default:
// 导航到首页
break;
}
}
}
// 在 main 中处理
void main() {
WidgetsFlutterBinding.ensureInitialized();
// 处理初始 deep link
final initialLink = await getInitialLink();
if (initialLink != null) {
DeepLinkHandler.handleDeepLink(Uri.parse(initialLink));
}
// 监听后续 deep link
linkStream.listen((link) {
if (link != null) {
DeepLinkHandler.handleDeepLink(Uri.parse(link));
}
});
runApp(const MyApp());
}
常见问题与解决方案
Q1:如何返回数据?
A:使用 Navigator.pop(context, result):
// 目标页面
Navigator.pop(context, 'result');
// 调用方
final result = await Navigator.push(context, route);
Q2:如何清除路由栈?
A:使用 pushNamedAndRemoveUntil:
Navigator.pushNamedAndRemoveUntil(
context,
'/home',
(route) => false, // 移除所有路由
);
Q3:如何传递复杂参数?
A:使用 arguments:
// 传递
Navigator.pushNamed(context, '/detail', arguments: {'id': 1, 'data': {...}});
// 获取
final args = ModalRoute.of(context)?.settings.arguments as Map;
最佳实践
1. 集中管理路由
// routes.dart
abstract class Routes {
static const home = '/';
static const login = '/login';
static const detail = '/detail';
}
// 使用
Navigator.pushNamed(context, Routes.detail);
2. 使用类型安全的参数
class DetailArguments {
final int id;
final String name;
DetailArguments({required this.id, required this.name});
}
// 传递
Navigator.pushNamed(
context,
Routes.detail,
arguments: DetailArguments(id: 1, name: 'Test'),
);
// 获取
final args = ModalRoute.of(context)?.settings.arguments as DetailArguments;
3. 使用 Navigator 扩展
extension NavigatorExtension on BuildContext {
void pushNamed(String routeName, {Object? arguments}) {
Navigator.pushNamed(this, routeName, arguments: arguments);
}
void pop([Object? result]) {
Navigator.pop(this, result);
}
}
// 使用
context.pushNamed(Routes.home);
context.pop();
总结
Flutter 的路由导航系统非常强大和灵活。通过本文的学习,你应该能够:
- 使用基础导航方法
- 配置和管理命名路由
- 创建自定义页面过渡动画
- 实现嵌套导航
- 处理 Deep Link
掌握这些技巧,能够帮助你创建更加流畅和用户友好的导航体验。
更多推荐
所有评论(0)