01
背景与动机
在Navigator 2.0
推出之前,Flutter
主要通过Navigator 1.0
和其提供的 API(如push()
, pop()
, pushNamed()
等)来管理页面路由。然而,Navigator 1.0
存在一些局限性,如难以实现复杂的页面操作(如移除栈内中间页面、交换页面等)、不支持嵌套路由以及无法满足全平台(尤其是Web
平台)的新需求。因此,Flutter
官方团队决定对路由系统进行改造,推出了Navigator 2.0
。
02
主要特性
声明式API
Navigator 2.0
提供的声明式API
使得路由管理更加直观和易于理解。开发者只需声明页面的配置信息,而无需编写复杂的导航逻辑代码。这种方式不仅减少了代码量,还提高了代码的可读性和可维护性。嵌套路由
Navigator 2.0
满足了嵌套路由的需求场景,允许开发者在应用中创建嵌套的路由结构。这使得应用的结构更加清晰,同时也提高了页面导航的灵活性。全平台支持
Navigator 2.0
提供的API
能够满足不同平台(如iOS
、Android
、Web
等)的导航需求,使得开发者能够更加方便地构建跨平台的应用。强大的页面操作能力
Navigator 2.0
提供了更加丰富的页面操作能力,如移除栈内中间页面、交换页面等。这些操作在Navigator 1.0
中很难实现或需要编写复杂的代码,而在Navigator 2.0
中则变得简单直接。
03
核心组件
Router 在
Navigator 2.0
中,Router
组件是路由管理的核心。它负责根据当前的路由信息(RouteInformation
)和路由信息解析器(RouteInformationParser
)来构建和更新UI
。Router
组件接收三个主要参数:1.routeInformationProvider:提供当前的路由信息;
2.routeInformationParser:将路由信息解析为路由配置;
3.routerDelegate:根据路由配置构建和更新
UI
。RouteInformationProvider
RouteInformationProvider
是一个提供当前路由信息的组件。它通常与平台相关的路由信息源(如浏览器的URL
、Android
的Intent
等)集成,以获取当前的路由信息。RouteInformationParser
RouteInformationParser
负责将RouteInformation
解析为RouteConfiguration
。这个过程允许开发者根据路由信息的格式(如URL
)来定义如何将其映射到应用内的路由配置。RouterDelegate
RouterDelegate
是与UI
构建紧密相关的组件。它必须实现RouterDelegate
接口,并提供两个主要方法:1.build(BuildContext context):根据当前的路由配置构建
UI
;2.setNewRoutePath(List configuration):设置新的路由路径,并更新
UI
;3.Future popRoute() :实现后退逻辑。
04
简单实例
首先通过MaterialApp.router()
来创建MaterialApp
:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
final routerDelegate = MyRouterDelegate();
final routeInformationParser = MyRouteInformationParser();
return MaterialApp.router(
title: 'Flutter Navigator 2.0 Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
routerDelegate: routerDelegate,
routeInformationParser: routeInformationParser,
);
}
}
需要定义一个RouterDelegate
对象和一个RouteInformationParser
对象。其中根据路由配置构建和更新UI
,RouteInformationParser
负责将RouteInformation
解析为RouteConfiguration
。 RouterDelegate
可以传个泛型,定义其currentConfiguration
对象的类型。
class MyRouterDelegate extends RouterDelegate<String>
with PopNavigatorRouterDelegateMixin<String>, ChangeNotifier {
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
private List<String> _pages = ['/home'];
@override
Widget build(BuildContext context) {
return Navigator(
key: navigatorKey,
pages: _pages.map((route) => MaterialPage(
key: Key(route),
child: generatePage(route),
)).toList(),
onPopPage: (route, result) {
if (!route.didPop(result)) {
return false;
}
_pages.removeLast();
notifyListeners();
return true;
},
);
}
@override
Future<void> setNewRoutePath(String path) async {
if (!_pages.contains(path)) {
_pages.add(path);
notifyListeners();
}
}
Widget generatePage(String route) {
switch (route) {
case '/home':
return HomePage();
case '/details':
// 这里可以传递参数,例如 DetailsPage(arguments: someData)
return DetailsPage();
default:
return NotFoundPage();
}
}
@override
String get currentConfiguration => _pages.last;
}
其中build()
一般返回的是一个Navigator
对象,popRoute()
实现后退逻辑,setNewRoutePath()
实现新页面的逻辑。定义了一个_pages
数组对象,记录每个路由的path
,可以理解为是一个路由栈,这个路由栈对我们来说非常友好,在有复杂的业务逻辑时,我们可以自行定义相应的栈管理逻辑。currentConfiguration
返回的是栈顶的page
信息。创建一个类继承RouteInformationParser
,主要的作用是包装解析路由信息,这里有一个最简单的方式,如下:
class MyRouteInformationParser extends RouteInformationParser<String> {
@override
Future<String> parseRouteInformation(RouteInformation routeInformation) {
final uri = Uri.parse(routeInformation.location);
return SynchronousFuture(uri.path);
}
@override
RouteInformation restoreRouteInformation(String configuration) {
return RouteInformation(location: configuration);
}
}
好的,接下来我们看一下调用:
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Home')),
body: Center(
child: ElevatedButton(
onPressed:&