Flutter路由管理插件theseus_navigator的使用
Flutter路由管理插件theseus_navigator的使用
概述
使用这些us导航器的起点是定义你的应用的导航方案。它可能看起来像这样:
目的地(Destination) 定义了用户在你的应用中可以到达的所有可能的UI端点。
导航控制器(NavigationController) 负责在其目的地范围内管理应用的导航状态。它执行导航操作,如 goTo(destination)
和 goBack()
,并构建导航堆栈。
导航方案(NavigationScheme) 是导航的入口点,它协调所有目的地和导航控制器。它有一个根导航控制器来管理顶级目的地,并且可选地具有额外的导航器以支持嵌套导航。
以下是使用示例:
定义目的地和导航方案
final homeDestination = Destination(
path: 'home',
isHome: true,
builder: (context, parameters) => HomeScreen(),
);
final catalogDestination = Destination(
path: 'catalog',
builder: (context, parameters) => CatalogScreen(),
);
final settingsDestination = Destination(
path: 'settings',
builder: (context, parameters) => SettingsScreen(),
);
final navigationScheme = NavigationScheme(
destinations: [
homeDestination,
catalogDestination,
settingsDestination,
],
);
设置带有导航方案的路由器
@override
Widget build(BuildContext context) {
return MaterialApp.router(
//...
routerConfig: navigationScheme.config,
);
}
在应用中的某个位置进行导航
onTap: () => navigationScheme.goTo(ordersDestination)
目的地(Destination)
目的地(Destination) 是一个模型,表示用户可以在你的应用中导航到的UI端点。
通常,你定义一个目的地如下:
final homeDestination = Destination(
path: 'home',
builder: (context, parameters) => HomeScreen(),
);
这是一个 最终目的地(final destination),它直接显示由提供的 builder
函数返回的内容。
嵌套导航(Nested Navigation)
对于嵌套导航,你应该提供一个 导航器(navigator),这应该是一个 NavigationController
。它管理自己的目的地,这些目的地可以是最终的,构建内容或过渡的,提供另一个嵌套导航器。
final mainDestination = Destination(
path: '/',
navigator: mainNavigator,
);
final mainNavigator = NavigationController(
destinations:[
homeDestination,
catalogDestination,
settingsDestination,
],
);
通过使用 Destination.transit()
构造函数,还可以将嵌套导航UI包装在一些额外的widget子树中。
final mainDestination = Destination.transit(
path: '/',
navigator: mainNavigator,
builder: (context, parameters, childBuilder) {
return Column(
children: [
const Text('Parent destination'),
Expanded(child: childBuilder(context)),
],
);
}
);
此构造函数有一个可选的 builder
参数,其中包含一个表示嵌套内容的 childBuilder
参数,必须包含在生成的widget树中。
路径(Path)
目的地由其 uri
定义,该 uri
由目的地的 path
和 parameters
构建。
路径可能包含用于路径参数的占位符。最后一个路径参数是可选的。还支持任意查询参数。
例如,目的地路径为:
/categories/{categoryId}
以下特定URI将匹配该目的地路径:
/categories
/categories/1
/categories?q=someQuery
/categories/1?q=someQuery
最后两个URI包含查询参数,但仍匹配指定的目的地路径。
参数(Parameters)
默认参数处理(Default Parameters Handling)
默认情况下,目的地参数(包括路径和查询)从目的地的URI提取到 DestinationParameters
类中。它们存储为 Map<String, String>
。
在这种情况下,你不需要指定一个目的地解析器,隐式使用 DefaultDestinationParser
实现。
final categoriesDestination = Destination(
path: 'categories',
builder: (context, params) => CategoryListScreen(
parentCategoryId: params?['parentId'],
),
);
例如,上述 URI categories?parentId=2
将被解析为一个目的地对象,该对象会构建一个 CategoryListScreen
并带有 parentCategoryId: 2
。
自定义类型参数(Custom Type Parameters)
要使用具有特定类型的参数的目的地,你需要做以下几件事:
- 创建一个扩展
DestinationParameters
的类,如下所示:
class CategoriesDestinationParameters extends DestinationParameters {
CategoriesDestinationParameters({
this.parentCategory,
}) : super();
final Category? parentCategory;
}
- 实现特定于你的参数类型的解析器:
class CategoriesDestinationParser extends DestinationParser<CategoriesDestinationParameters> {
CategoriesDestinationParser({
required this.categoryRepository,
});
final CategoryRepository categoryRepository;
@override
Future<CategoriesDestinationParameters> parametersFromMap(Map<String, String> map) async {
final category = await categoryRepository.getCategory(map['parentCategoryId'] ?? '');
return CategoriesDestinationParameters(
parentCategory: category,
);
}
@override
Map<String, String> parametersToMap(CategoryListParameters parameters) {
final result = <String, String>{};
if (parameters.parentCategory != null) {
result['parentCategoryId'] = parameters.parentCategory!.id;
}
return result;
}
}
- 创建你的目的地如下:
final categoriesDestination = Destination<CategoriesDestinationParameters>(
path: 'categories',
builder: (context, params) => CategoryListScreen(
parentCategory: params?.parentCategory,
),
parser: CategoriesDestinationParser(
categoryRepository: CategoryRepository(),
),
);
设置(Settings)
目的地设置(DestinationSettings) 包含用于确定更新导航状态的逻辑和行为的属性。
transitionMethod
- 定义导航到目的地时如何更改导航堆栈,可以是 push
或 replace
。
transition
- 当目的地内容出现时应用的动画,可以是 material
、custom
或 none
。
如果使用 custom
过渡,则还需要提供 transitionBuilder
。
有两个预定义的工厂方法:
material()
- 返回将目的地推送到堆栈的标准Material动画。dialog()
- 将目的地作为模态对话框显示。quiet()
- 用新目的地替换当前目的地而不使用任何动画。
final catalogDestination = Destination(
path: 'catalog',
builder: (context, parameters) => CatalogScreen(),
settings: const DestinationSettings.quiet(),
);
导航控制器(NavigationController)
导航控制器(NavigationController) 是包的核心组件,负责管理导航状态。
它维护导航堆栈中的目的地,并提供更新堆栈的方法,如 goTo(destination)
和 goBack()
。
导航控制器是一个 ChangeNotifier
,并在其导航堆栈的任何更新时通知 NavigationScheme
。
你可以访问整个导航堆栈和堆栈顶部的目的地使用 currentDestination
属性。
此示例显示了创建一个将目的地包装在标准Flutter Navigator
widget中的导航器:
final mainNavigator = NavigationController(
destinations: [
homeDestination,
catalogDestination,
settingsDestination,
],
tag: 'Main',
);
tag
属性值用作 Navigator
widget的键,并且也允许找到与此 NavigationController
实例相关的日志。
导航构建器(Navigator Builder)
导航控制器允许你使用自定义导航UI包装目的地。
当你希望通过 BottomNavigationBar
、TabBar
、Drawer
或其他方式导航目的地时,这是必需的。
为此,你必须扩展 NavigatorBuilder
类并覆盖其 build
方法:
class CustomNavigatorBuilder extends NavigatorBuilder {
const CustomNavigatorBuilder() : super();
@override
Widget build(BuildContext context, NavigationController navigator) {
// 你的wrapper widget实现在这里。
// 你可以访问导航器的堆栈和当前目的地。
}
}
然后你应该在导航器实例中指定 builder
:
final mainNavigator = NavigationController(
destinations: [
homeDestination,
catalogDestination,
settingsDestination,
],
builder: CustomNavigatorBuilder(),
tag: 'Main',
);
通过底部导航栏、抽屉和标签栏进行导航
包包括最常见的 NavigatorBuilder
实现:
BottomNavigationBuilder
- 使用Flutter的Scaffold
和BottomNavigationBar
或Material 3NavigationBar
来包装当前目的地内容并切换目的地。DrawerNavigationBuilder
- 允许使用Drawer
widget进行导航。TabsNavigationBuilder
- 使用TabBar
进行导航目的地。
例如,向你的应用添加底部导航栏非常简单:
final navigationScheme = NavigationScheme(
navigator: NavigationController(
destinations: [
homeDestination,
catalogDestination,
settingsDestination,
],
builder: BottomNavigationBuilder(
bottomNavigationItems: const [
BottomNavigationBarItem(
icon: Icon(Icons.home_rounded),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.list_rounded),
label: 'Catalog',
),
BottomNavigationBarItem(
icon: Icon(Icons.more_horiz_rounded),
label: 'Settings',
),
],
),
tag: 'Main',
),
);
BottomNavigationBar
widget的样式可以通过 BottomNavigationBuilder
的可选 parameters
参数来支持。
你可以以相同的方式使用 DrawerNavigationBuilder
和 TabsNavigationBuilder
。
向上导航(Upward Navigation)
有时,在从用户跳过底层目的地的某个目的地反向导航时,我们需要恢复缺失的目的地层次结构。
例如,用户通过深度链接打开应用,该链接导致某个类别屏幕位于类别层次结构中。从该屏幕导航回时,我们希望显示上级类别屏幕,依此类推直到类别根目录。
当为目的地定义 upwardDestinationBuilder
参数时,包支持这种行为。
它可能看起来像这样:
final categoriesDestination = Destination<CategoryListParameters>(
path: 'categories',
builder: (context, params) => CategoryListScreen(
parentCategory: params?.category,
),
upwardDestinationBuilder: (destination) =>
destination.parameters?.parentCategory == null
? null
: destination.copyWithParameters(CategoriesDestinationParameters(
parentCategory:
destination.parameters?.parentCategory!.parent)),
parser: CategoriesDestinationParser(
categoryRepository: CategoryRepository(),
),
);
深度链接(Deep-links)
当平台请求一个目的地时,对于从当前目的地到根的每个导航器层次结构,将发生以下情况:
- 当前目的地根据请求的深度链接目的地进行更新。
- 如果当前目的地提供了
upwardDestinationBuilder
参数,则在添加新目的地之前清空导航器堆栈。否则,当前目的地将被推送到现有堆栈中。
重定向(Redirections)
有时我们需要在显示请求的内容之前将用户重定向到另一个屏幕。
基本示例是某些屏幕只应显示给已登录的用户。
包提供了 Redirection
类以支持此行为。你可以为应在导航之前验证的目的地指定一个重定向列表。
final settingsDestination = Destination(
path: 'settings',
builder: (context, parameters) => SettingsScreen(),
redirections: [
Redirection(
validator: (destination) => SynchronousFuture(isLoggedIn),
destination: loginDestination,
)
]
);
在上述示例中,当用户导航到设置屏幕时,首先将调用指定重定向的 validator
函数。如果它返回 false
,则用户将被重定向到登录屏幕。
重定向中的验证函数可以是异步的。如果它运行时间较长,等待覆盖小部件将在目标解决之前显示。你可以通过提供 NavigationScheme
的 waitingOverlayBuilder
参数来自定义等待覆盖小部件。
也可以通过提供 resolver
函数而不是固定的 destination
动态识别重定向的目的地。
错误处理(Error Handling)
如果在 NavigationScheme
中指定了 errorDestination
,则当尝试导航到不存在的屏幕时,用户将被重定向到此屏幕。
final navigationScheme = NavigationScheme(
destinations: [
//...
],
errorDestination: Destination(
path: '/error',
builder: (context, parameters) => ErrorScreen(),
)
);
更多关于Flutter路由管理插件theseus_navigator的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter路由管理插件theseus_navigator的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是一个关于如何使用Flutter路由管理插件theseus_navigator
的代码示例。theseus_navigator
是一个功能强大的路由管理插件,它支持多种导航模式,并且易于集成到Flutter应用中。
1. 添加依赖
首先,你需要在pubspec.yaml
文件中添加theseus_navigator
的依赖:
dependencies:
flutter:
sdk: flutter
theseus_navigator: ^最新版本号
然后运行flutter pub get
来安装依赖。
2. 配置路由
接下来,你需要配置路由。在一个新的Flutter项目中,你可以在lib
目录下创建一个router
文件夹,并在其中创建一个router.dart
文件来配置路由。
// lib/router/router.dart
import 'package:flutter/material.dart';
import 'package:theseus_navigator/theseus_navigator.dart';
import 'package:your_app/screens/home_screen.dart';
import 'package:your_app/screens/detail_screen.dart';
class AppRouter {
static final navigatorKey = GlobalKey<NavigatorState>();
static final routes = Routes(
routes: {
'/': (context) => HomeScreen(),
'/detail': (context, {required String id}) => DetailScreen(id: id),
},
);
static MaterialApp getMaterialApp(BuildContext context) {
return MaterialApp.router(
routeInformationParser: routes.routeInformationParser,
routerDelegate: routes.routerDelegate.withNavigator(navigatorKey),
);
}
}
3. 创建屏幕
然后,你需要创建一些屏幕组件,比如HomeScreen
和DetailScreen
。
// lib/screens/home_screen.dart
import 'package:flutter/material.dart';
import 'package:theseus_navigator/theseus_navigator.dart';
import 'package:your_app/router/router.dart';
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home Screen'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
// Navigate to detail screen with an example ID
AppRouter.routes.navigator.pushNamed(
'/detail',
arguments: {'id': '123'},
);
},
child: Text('Go to Detail'),
),
),
);
}
}
// lib/screens/detail_screen.dart
import 'package:flutter/material.dart';
import 'package:your_app/router/router.dart';
class DetailScreen extends StatelessWidget {
final String id;
DetailScreen({required this.id});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Detail Screen'),
),
body: Center(
child: Text('Detail ID: $id'),
),
);
}
}
4. 使用路由
最后,在你的main.dart
文件中使用配置好的路由。
// lib/main.dart
import 'package:flutter/material.dart';
import 'package:your_app/router/router.dart';
void main() {
runApp(AppRouter.getMaterialApp(context: null)); // Note: context is null here because we're not using it directly
}
注意:在main.dart
中传递context
给AppRouter.getMaterialApp
时,由于main
函数的上下文还没有被创建,所以直接传递null
或者使用其他方式初始化(例如通过builder
参数)。然而,theseus_navigator
通常不需要在main
函数中直接处理context
,因为它使用GlobalKey
来管理导航器状态。
5. 运行应用
现在,你可以运行你的Flutter应用,点击Home Screen
上的按钮应该会导航到Detail Screen
并显示传递的ID。
这是一个基本的示例,展示了如何使用theseus_navigator
进行路由管理。根据你的具体需求,你可以进一步扩展和自定义路由配置。