引言
對于Flutter路由的使用,從入門到深入一般有以下幾個階段:
- 初步了解到Flutter中是用路由(Route)表示頁面(Page)。使用基本的MaterialPageRoute方式進行頁面操作。
- 學會使用Navigator來完成路由操作。
- 學會使用命名路(Named Route)由來完成路由的管理與操作。
以上三個階段屬于官方的基本知識。 - 使用三方Fluro完成路由的管理與操作。
- 使用注解方式完成路由的管理與操作。(閑魚有三方)
本文在已經(jīng)完成前三個階段的狀態(tài)下,闡述Fluro的使用。
首先假設(shè)我們的App組織架構(gòu)如下
- login模塊
- home模塊
- buy模塊
我們需要高效的管理現(xiàn)有頁面路由,并兼顧后續(xù)頁面擴展。
先來看Fluro的基本使用,進而分析為了完成我們的目標需要做哪些額外的工作
新建一個Flutter工程
在pubspec.yaml文件中添加Fluro三方
dependencies:
flutter:
sdk: flutter
# 路由管理三方Fluro
# https://github.com/theyakka/fluro
fluro: ^1.6.3
.yaml文件對齊有嚴格要求
建議每個添加的三方庫都附上簡要說明和網(wǎng)址
修改基本代碼
修改Home頁面布局為中間一個按鈕
添加一個Buy頁面,布局也是中間一個按鈕
源碼lib1
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: FlatButton(
onPressed: () => print('進行跳轉(zhuǎn)操作'),
child: Text('點我 跳轉(zhuǎn)到buy頁面'),
),
),
);
}
}
根據(jù)作者的說明我們先初始化Router
final router = Router();
接下來我們?yōu)g覽一下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方法接受一些參數(shù),返回了一個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);
這就是非?;A(chǔ)的Navigator用法了。
context是直接傳入的參數(shù),那么route是如何得到呢。
RouteMatch routeMatch = matchRoute(context, path,
transitionType: transition,
transitionsBuilder: transitionBuilder,
transitionDuration: transitionDuration);
Route<dynamic> route = routeMatch.route;
可以看到通過傳入path等參數(shù),通過使用matchRoute方法,我們獲得到了route。暫時不用關(guān)心該方法的內(nèi)部,大概的也能猜到path就是我們使用命名路由,注冊路由表時的路由名。此時我們還沒有使用命名路由注冊路由表。
讓我們直接使用作者給我們的說明代碼
router.navigateTo(context, "/users/1234", transition: TransitionType.fadeIn);
看看報錯信息
flutter: No registered route was found to handle '/users/1234'.
flutter: 進行跳轉(zhuǎn)操作
[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在哪里執(zhí)行。
查看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 用來存儲已經(jīng)定義的路由
difine方法創(chuàng)建PageRoute
那么我們需要調(diào)用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是構(gòu)造成HandlerType.route類型的類,需要傳入this.handlerFunc參數(shù)
HandlerFunc是一個Widget,是BuildContext context, Map<String, List<String>> parameters的typedef定義。因此,我們要傳送一個BuildContext context, Map<String, List<String>> parameters給Handler當參數(shù)。
讓我們構(gòu)造一個buyPageHandler,參考作者的代碼,返回值是一個路由Wiget(BuyPage())
var buyPageHandler = Handler(
handlerFunc: (BuildContext context, Map<String, dynamic> params) {
return BuyPage();
});
此時,修改路由定義代碼
router.define('/users/1234', handler: buyPageHandler);
運行程序 查看代碼
可以看到,點擊HomePage的按鈕已經(jīng)正常能夠跳轉(zhuǎn)到BuyPage。
讓我們回一下為了,實現(xiàn)跳轉(zhuǎn)Fluro都做了哪些事情。
- 初始化了一個router
- 定義了一個buyPageHandler??梢詡魅雙arams,返回一個route類型的Widget.
- 定義了一個router,需要傳入name和handler兩個參數(shù)
- 使用navigateTo,傳入name,進行頁面跳轉(zhuǎn)。
至此Fluro的最基本用法已經(jīng)說明完畢。讓我們回到最初的那個問題
我們的App組織架構(gòu)如下
- login模塊 - home模塊 - buy模塊我們需要高效的管理現(xiàn)有頁面路由,并兼顧后續(xù)頁面擴展。
如果我們想在BuyPage跳轉(zhuǎn)到HomePage頁面,那么剛才的4個步驟我們需要一個不落的重新一遍。重新就意味著可以抽離相同的代碼。比如抽離route的初始化。
如果我們想在LoginPage頁面也跳轉(zhuǎn)到HomePage頁面,那么步驟 2 3也可以抽離出來單獨定義,從而進行復用。我們可以清晰的看到步驟 2 3是跳轉(zhuǎn)目標頁面的定義,而與到底從哪個頁面跳轉(zhuǎn)來無關(guān)。
如何進行抽離呢?作者的說明里有這么兩句話。。
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.