Flutter路由管理三方Fluro使用
引言
对于Flutter路由的使用,从入门到深入一般有以下几个阶段:
- 初步了解到Flutter中是用路由(Route)表示页面(Page)。使用基本的MaterialPageRoute方式进行页面操作。
- 学会使用Navigator来完成路由操作。
- 学会使用命名路(Named Route)由来完成路由的管理与操作。
以上三个阶段属于官方的基本知识。 - 使用三方Fluro完成路由的管理与操作。
- 使用注解方式完成路由的管理与操作。(闲鱼有三方)
本文在已经完成前三个阶段的状态下,阐述Fluro的使用。
首先假设我们的App组织架构如下
- login模块
- home模块
- buy模块
我们需要高效的管理现有页面路由,并兼顾后续页面扩展。
先来看Fluro的基本使用,进而分析为了完成我们的目标需要做哪些额外的工作
新建一个Flutter工程
在pubspec.yaml文件中添加Fluro三方
dependencies:
flutter:
sdk: flutter
# 路由管理三方Fluro
# https://github.com/theyakka/fluro
fluro: ^1.6.3
.yaml文件对齐有严格要求
建议每个添加的三方库都附上简要说明和网址
修改基本代码
修改Home页面布局为中间一个按钮
添加一个Buy页面,布局也是中间一个按钮
源码lib1
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: FlatButton(
onPressed: () => print('进行跳转操作'),
child: Text('点我 跳转到buy页面'),
),
),
);
}
}
根据作者的说明我们先初始化Router
final router = Router();
接下来我们浏览一下Router的源码,大致浏览都提供了哪些方法。
...假装看懂了,还是看作者说明吧。作者说明对新手也不怎么友好。。
好像有一行很短的样子,先看这一行。
You can also manually push to a route yourself. To do so:
router.navigateTo(context, "/users/1234", transition: TransitionType.fadeIn);
翻看一下源代码
///
Future navigateTo(BuildContext context, String path,
{bool replace = false,
bool clearStack = false,
TransitionType transition,
Duration transitionDuration = const Duration(milliseconds: 250),
RouteTransitionsBuilder transitionBuilder}) {
....
return future;
}
作者留了个///给我们,忧伤
navigateTo方法接受一些参数,返回了一个future.
让我们看看future是个什么
if (route != null) {
if (clearStack) {
future =
Navigator.pushAndRemoveUntil(context, route, (check) => false);
} else {
future = replace
? Navigator.pushReplacement(context, route)
: Navigator.push(context, route);
}
...
}
默认的clearStack是false,replace是false,那么
future = Navigator.push(context, route);
这就是非常基础的Navigator用法了。
context是直接传入的参数,那么route是如何得到呢。
RouteMatch routeMatch = matchRoute(context, path,
transitionType: transition,
transitionsBuilder: transitionBuilder,
transitionDuration: transitionDuration);
Route<dynamic> route = routeMatch.route;
可以看到通过传入path等参数,通过使用matchRoute方法,我们获得到了route。暂时不用关心该方法的内部,大概的也能猜到path就是我们使用命名路由,注册路由表时的路由名。此时我们还没有使用命名路由注册路由表。
让我们直接使用作者给我们的说明代码
router.navigateTo(context, "/users/1234", transition: TransitionType.fadeIn);
看看报错信息
flutter: No registered route was found to handle '/users/1234'.
flutter: 进行跳转操作
[VERBOSE-2:ui_dart_state.cc(157)] Unhandled Exception:
No registered route was found to handle '/users/1234'
null
提示我们路由没有注册。这个提示是fluro给出的。
是否就是使用命名路由时的注册路由表呢?我们添加一下代码
routes: {
'/users/1234': (context) => BuyPage(),
},
错误依然存在。那么让我们来看看fluro说的registered在哪里执行。
查看fluro的源码
/// The tree structure that stores the defined routes
final RouteTree _routeTree = RouteTree();
...
/// Creates a [PageRoute] definition for the passed [RouteHandler].
/// You can optionally provide a default transition type.
void define(String routePath,
{@required Handler handler, TransitionType transitionType}) {
_routeTree.addRoute(
AppRoute(routePath, handler, transitionType: transitionType),
);
}
可以看到作者的解释
RouteTree 用来存储已经定义的路由
difine方法创建PageRoute
那么我们需要调用define方法来定义(registered)路由。
Widget build(BuildContext context) {
...
router.define('/users/1234', handler: XXX);
...
}
那么这个handler是什么呢?查看一下源码
class Handler {
Handler({this.type = HandlerType.route, this.handlerFunc});
final HandlerType type;
final HandlerFunc handlerFunc;
}
...
typedef Widget HandlerFunc(
BuildContext context, Map<String, List<String>> parameters);
Handler是构造成HandlerType.route类型的类,需要传入this.handlerFunc参数
HandlerFunc是一个Widget,是BuildContext context, Map<String, List<String>> parameters的typedef定义。因此,我们要传送一个BuildContext context, Map<String, List<String>> parameters给Handler当参数。
让我们构造一个buyPageHandler,参考作者的代码,返回值是一个路由Wiget(BuyPage())
var buyPageHandler = Handler(
handlerFunc: (BuildContext context, Map<String, dynamic> params) {
return BuyPage();
});
此时,修改路由定义代码
router.define('/users/1234', handler: buyPageHandler);
运行程序 查看代码
可以看到,点击HomePage的按钮已经正常能够跳转到BuyPage。
让我们回一下为了,实现跳转Fluro都做了哪些事情。
- 初始化了一个router
- 定义了一个buyPageHandler。可以传入params,返回一个route类型的Widget.
- 定义了一个router,需要传入name和handler两个参数
- 使用navigateTo,传入name,进行页面跳转。
至此Fluro的最基本用法已经说明完毕。让我们回到最初的那个问题
我们的App组织架构如下
- login模块 - home模块 - buy模块
我们需要高效的管理现有页面路由,并兼顾后续页面扩展。
如果我们想在BuyPage跳转到HomePage页面,那么刚才的4个步骤我们需要一个不落的重新一遍。重新就意味着可以抽离相同的代码。比如抽离route的初始化。
如果我们想在LoginPage页面也跳转到HomePage页面,那么步骤 2 3也可以抽离出来单独定义,从而进行复用。我们可以清晰的看到步骤 2 3是跳转目标页面的定义,而与到底从哪个页面跳转来无关。
如何进行抽离呢?作者的说明里有这么两句话。。
It may be convenient for you to store the router globally/statically so that you can access the router in other areas in your application.
You can use the Router with the MaterialApp.onGenerateRoute parameter via the Router.generator function. To do so, pass the function reference to the onGenerate parameter like: onGenerateRoute: router.generator.