4.1 Navigator 与 Route
·
Flutter 的导航系统分为 Navigator 1.0(命令式)和 Navigator 2.0(声明式)两代。理解两者的差异有助于选择合适的导航方案。
一、Navigator 1.0(命令式导航)
Navigator 1.0 是传统的命令式导航,通过 push/pop 操作路由栈。
1.1 基本导航操作
// push:压栈(进入新页面)
Navigator.push(
context,
MaterialPageRoute(builder: (_) => const DetailPage()),
);
// pop:出栈(返回上一页)
Navigator.pop(context);
// pop 并传递返回值
Navigator.pop(context, 'result_data');
// 接收返回值
final result = await Navigator.push<String>(
context,
MaterialPageRoute(builder: (_) => const EditPage()),
);
print('Returned: $result');
// pushReplacement:替换当前页面(不可返回)
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (_) => const HomePage()),
);
// pushAndRemoveUntil:清空路由栈跳转
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (_) => const LoginPage()),
(route) => false, // false = 清空所有
);
// popUntil:返回到指定路由
Navigator.popUntil(context, ModalRoute.withName('/home'));
1.2 命名路由(pushNamed)
// 注册路由表
MaterialApp(
initialRoute: '/',
routes: {
'/': (context) => const HomePage(),
'/product': (context) => const ProductPage(),
'/login': (context) => const LoginPage(),
'/profile': (context) => const ProfilePage(),
},
)
// 使用命名路由导航
Navigator.pushNamed(context, '/product');
// 传递参数
Navigator.pushNamed(
context,
'/product',
arguments: {'productId': 123, 'from': 'home'},
);
// 在目标页面接收参数
class ProductPage extends StatelessWidget {
Widget build(BuildContext context) {
final args = ModalRoute.of(context)!.settings.arguments
as Map<String, dynamic>;
final productId = args['productId'] as int;
return Text('Product $productId');
}
}
1.3 onGenerateRoute(动态路由生成)
MaterialApp(
onGenerateRoute: (settings) {
// 解析路由路径,支持动态参数
final uri = Uri.parse(settings.name ?? '/');
switch (uri.pathSegments.first) {
case 'product':
final id = int.tryParse(uri.pathSegments.last);
return MaterialPageRoute(
builder: (_) => ProductDetailPage(productId: id!),
settings: settings,
);
case 'category':
final slug = uri.pathSegments.last;
return MaterialPageRoute(
builder: (_) => CategoryPage(slug: slug),
);
default:
return MaterialPageRoute(builder: (_) => const NotFoundPage());
}
},
onUnknownRoute: (settings) => MaterialPageRoute(
builder: (_) => const NotFoundPage(),
),
)
二、Navigator 2.0(声明式导航)
Navigator 2.0 将路由变成了"状态",通过声明式方式描述路由栈,更适合 Web 端的 URL 管理和深链。
2.1 RouterDelegate
class AppRouterDelegate extends RouterDelegate<AppRoutePath>
with ChangeNotifier, PopNavigatorRouterDelegateMixin<AppRoutePath> {
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
AppRoutePath _currentPath = AppRoutePath.home();
// 当前路由状态
AppRoutePath get currentPath => _currentPath;
void navigateTo(AppRoutePath path) {
_currentPath = path;
notifyListeners(); // 通知导航系统重建路由栈
}
AppRoutePath get currentConfiguration => _currentPath;
Widget build(BuildContext context) {
// 声明式:根据状态描述路由栈
return Navigator(
key: navigatorKey,
pages: [
const MaterialPage(key: ValueKey('home'), child: HomePage()),
if (_currentPath.isProduct)
MaterialPage(
key: ValueKey('product_${_currentPath.productId}'),
child: ProductDetailPage(productId: _currentPath.productId!),
),
],
onPopPage: (route, result) {
if (!route.didPop(result)) return false;
navigateTo(AppRoutePath.home());
return true;
},
);
}
Future<void> setNewRoutePath(AppRoutePath path) async {
_currentPath = path;
}
}
2.2 RouteInformationParser
class AppRouteInformationParser extends RouteInformationParser<AppRoutePath> {
Future<AppRoutePath> parseRouteInformation(RouteInformation info) async {
final uri = Uri.parse(info.location ?? '/');
if (uri.pathSegments.isEmpty) return AppRoutePath.home();
if (uri.pathSegments.first == 'product' && uri.pathSegments.length == 2) {
return AppRoutePath.product(int.parse(uri.pathSegments[1]));
}
return AppRoutePath.unknown();
}
RouteInformation? restoreRouteInformation(AppRoutePath path) {
if (path.isHome) return const RouteInformation(location: '/');
if (path.isProduct) return RouteInformation(location: '/product/${path.productId}');
return const RouteInformation(location: '/404');
}
}
三、MaterialPageRoute 与自定义 Route
3.1 MaterialPageRoute
// 标准 Material 风格过渡(Android:底部滑入;iOS:右侧滑入)
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const DetailPage(),
fullscreenDialog: true, // iOS 全屏模态样式(从底部滑入)
maintainState: false, // 返回时是否保留状态
),
)
3.2 CupertinoPageRoute
// iOS 风格过渡(右侧滑入,支持边缘滑动返回)
Navigator.push(
context,
CupertinoPageRoute(builder: (_) => const DetailPage()),
)
3.3 自定义过渡动画
class FadeScaleRoute<T> extends PageRouteBuilder<T> {
final Widget page;
FadeScaleRoute({required this.page})
: super(
pageBuilder: (_, __, ___) => page,
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return FadeTransition(
opacity: animation,
child: ScaleTransition(
scale: Tween<double>(begin: 0.9, end: 1.0).animate(
CurvedAnimation(parent: animation, curve: Curves.easeOut),
),
child: child,
),
);
},
transitionDuration: const Duration(milliseconds: 250),
reverseTransitionDuration: const Duration(milliseconds: 200),
);
}
四、路由栈管理
4.1 路由栈常用操作
| 操作 | 方法 | 场景 |
|---|---|---|
| 入栈 | push | 进入新页面,可返回 |
| 出栈 | pop | 返回上一页 |
| 替换 | pushReplacement | 登录成功后替换登录页 |
| 清空入栈 | pushAndRemoveUntil | 退出登录后清空栈 |
| 出栈到指定 | popUntil | 多级返回到主页 |
| 查看栈顶 | canPop() | 判断是否可以返回 |
4.2 嵌套 Navigator
class MainPage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
body: Row(
children: [
NavigationPanel(),
Expanded(
child: Navigator( // 嵌套的 Navigator,用于右侧内容区
initialRoute: 'dashboard',
onGenerateRoute: (settings) {
return MaterialPageRoute(
builder: (_) => switch (settings.name) {
'dashboard' => const DashboardPage(),
'settings' => const SettingsPage(),
_ => const NotFoundPage(),
},
);
},
),
),
],
),
);
}
}
小结
| 特性 | Navigator 1.0 | Navigator 2.0 |
|---|---|---|
| 编程范式 | 命令式 | 声明式 |
| 适用场景 | 普通移动 App | Web / 深链 / 复杂路由 |
| 学习成本 | 低 | 高 |
| URL 支持 | 有限 | 完整 |
| 第三方支持 | GoRouter / AutoRoute | GoRouter(推荐) |
👉 下一节:4.2 命名路由与参数传递
更多推荐
所有评论(0)