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. 根据配置构建 Navigatorclass 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 中使用 Routervoid 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/