702 字
4 分钟
Flutter Navigator v2
Navigator 2.0(也称为 Router API),它与传统的 imperative(命令式)Navigator 1.0 不同,采用声明式的思路,更好地支持 URL 路由、Web 与桌面多窗口,以及深度链接。
⸻
概念
- Router
顶层 Widget,负责接收外部路由变化(如浏览器地址栏、系统返回、深度链接)并将它们分发给下面的两个组件:
- RouteInformationParser
- RouterDelegate
- RouteInformationParser
把外部的 RouteInformation(通常包含一个 URI 字符串)解析成你的「配置模型」(configuration),比如一个自定义的
MyRoutePath
对象。 - RouterDelegate
根据当前的「配置模型」构建并返回一个 Navigator,通常是一个带有
pages: List<Page>
的声明式导航栈。你在这里决定要哪些页面、以何种顺序展示。 - BackButtonDispatcher (可选)用于处理 Android/iOS 的返回按钮或 Web 上的前进后退。
- Page & Route
Navigator 2.0 推荐使用 Page 对象(如
MaterialPage
、CupertinoPage
)来描述页面,而不是直接推Route
。
示例
import 'package:flutter/material.dart';
// 1. 定义路由配置模型
class MyRoutePath {
final String? location;
MyRoutePath.home() : location = '/';
MyRoutePath.details(this.location);
}
// 2. 解析 URL → 配置
class MyRouteParser extends RouteInformationParser<MyRoutePath> {
@override
Future<MyRoutePath> parseRouteInformation(
RouteInformation routeInformation) async {
final uri = Uri.parse(routeInformation.location ?? '/');
if (uri.pathSegments.isEmpty) {
return MyRoutePath.home();
} else {
return MyRoutePath.details('/' + uri.pathSegments.join('/'));
}
}
@override
RouteInformation restoreRouteInformation(MyRoutePath configuration) {
return RouteInformation(location: configuration.location);
}
}
// 3. 根据配置构建 Navigator
class MyRouterDelegate extends RouterDelegate<MyRoutePath>
with ChangeNotifier, PopNavigatorRouterDelegateMixin<MyRoutePath> {
final GlobalKey<NavigatorState> navigatorKey;
String? _selectedLocation;
MyRouterDelegate() : navigatorKey = GlobalKey<NavigatorState>();
MyRoutePath get currentConfiguration {
if (_selectedLocation == null) {
return MyRoutePath.home();
}
return MyRoutePath.details(_selectedLocation);
}
@override
Widget build(BuildContext context) {
return Navigator(
key: navigatorKey,
pages: [
// 始终存在的首页
MaterialPage(key: ValueKey('HomePage'), child: HomePage(onTap: _handleTap)),
// 如果有选中,就在栈顶加一个详情页
if (_selectedLocation != null)
MaterialPage(
key: ValueKey('DetailsPage'),
child: DetailsPage(location: _selectedLocation!),
),
],
onPopPage: (route, result) {
if (!route.didPop(result)) return false;
// 处理返回:清空选中,刷新
_selectedLocation = null;
notifyListeners();
return true;
},
);
}
void _handleTap(String location) {
_selectedLocation = location;
notifyListeners();
}
@override
Future<void> setNewRoutePath(MyRoutePath configuration) async {
if (configuration.location == '/') {
_selectedLocation = null;
} else {
_selectedLocation = configuration.location;
}
}
}
// 4. 定义首页和详情页
class HomePage extends StatelessWidget {
final void Function(String) onTap;
HomePage({required this.onTap});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('首页')),
body: ListView(
children: ['foo', 'bar', 'baz']
.map((e) => ListTile(title: Text(e), onTap: () => onTap(e)))
.toList(),
),
);
}
}
class DetailsPage extends StatelessWidget {
final String location;
DetailsPage({required this.location});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('详情:$location')),
body: Center(child: Text('当前路由:$location')),
);
}
}
// 5. 在 MaterialApp 中使用 Router
void main() {
runApp(MaterialApp.router(
routeInformationParser: MyRouteParser(),
routerDelegate: MyRouterDelegate(),
));
}
使用
- 定义“路由状态”模型,表示你的页面或子页面状态(如 MyRoutePath)。
- 实现 RouteInformationParser,把 URL ↔ 状态模型 相互转换。
- 实现 RouterDelegate,在 build() 中返回一个声明式的 Navigator(pages: […]),并在用户交互或路由变化时通过 notifyListeners() 更新。
- 在 MaterialApp.router(或 CupertinoApp.router)中指定这两个组件。
这样就完成了一个最基础的 Navigator 2.0 架构,你可以在此基础上扩展子路由、嵌套路由、命名路由、守卫、404 页面、Web 深度链接等功能。
Flutter Navigator v2
https://blog.lpkt.cn/posts/flutter-navigator-v2/