1. 應用場景
開發中經常遇到
- 路由跳轉時拿不到context怎麼辦,eg: token失效/異地登入跳轉登入頁面。
- 獲取不到當前路由名稱怎麼辦,eg: 點選push推送跳轉指定路由,如果已經在當前頁面就replace,如果不在就push。
- 註冊監聽路由跳轉,做一些想做的事情 ,eg:不同路由,顯示不同狀態列顏色。
- 等等...
2. 解決方案
解決思路:
- MaterialApp 的routes屬性賦值路由陣列,navigatorObservers屬性賦值路由監聽物件NavigatorManager。
- 在NavigatorManager裡實現NavigatorObserver的didPush/didReplace/didPop/didRemove,並記錄到路由棧 List _mRoutes中。
- 將實時記錄的路由跳轉,用stream發一個廣播,哪裡需要哪裡註冊。
3. 具體實現
main.dart
MaterialApp(
navigatorObservers: [NavigatorManager.getInstance()],
routes: NavigatorManager.configRoutes,
...
)
複製程式碼
navigator_manager.dart
class NavigatorManager extends NavigatorObserver {
/* 配置routes */
static Map<String, WidgetBuilder> configRoutes = {
PackageInfoPage.sName: (context) =>
SplashPage.sName: (context) => SplashPage(),
LoginPage.sName: (context) => SplashPage()),
MainPage.sName: (context) => SplashPage(),
//...
}
// 當前路由棧
static List<Route> _mRoutes;
List<Route> get routes => _mRoutes;
// 當前路由
Route get currentRoute => _mRoutes[_mRoutes.length - 1];
// stream相關
static StreamController _streamController;
StreamController get streamController=> _streamController;
// 用來路由跳轉
static NavigatorState navigator;
/* 單例給出NavigatorManager */
static NavigatorManager navigatorManager;
static NavigatorManager getInstance() {
if (navigatorManager == null) {
navigatorManager = new NavigatorManager();
_streamController = StreamController.broadcast();
}
return navigatorManager;
}
// replace 頁面
pushReplacementNamed(String routeName, [WidgetBuilder builder]) {
return navigator.pushReplacement(
CupertinoPageRoute(
builder: builder ?? configRoutes[routeName],
settings: RouteSettings(name: routeName),
),
);
}
// push 頁面
pushNamed(String routeName, [WidgetBuilder builder]) {
return navigator.push(
CupertinoPageRoute(
builder: builder ?? configRoutes[routeName],
settings: RouteSettings(name: routeName),
),
);
}
// pop 頁面
pop<T extends Object>([T result]) {
navigator.pop(result);
}
// push一個頁面, 移除該頁面下面所有頁面
pushNamedAndRemoveUntil(String newRouteName) {
return navigator.pushNamedAndRemoveUntil(newRouteName, (Route<dynamic> route) => false);
}
// 當呼叫Navigator.push時回撥
@override
void didPush(Route route, Route previousRoute) {
super.didPush(route, previousRoute);
if (_mRoutes == null) {
_mRoutes = new List<Route>();
}
// 這裡過濾調push的是dialog的情況
if (route is CupertinoPageRoute || route is MaterialPageRoute) {
_mRoutes.add(route);
routeObserver();
}
}
// 當呼叫Navigator.replace時回撥
@override
void didReplace({Route newRoute, Route oldRoute}) {
super.didReplace();
if (newRoute is CupertinoPageRoute || newRoute is MaterialPageRoute) {
_mRoutes.remove(oldRoute);
_mRoutes.add(newRoute);
routeObserver();
}
}
// 當呼叫Navigator.pop時回撥
@override
void didPop(Route route, Route previousRoute) {
super.didPop(route, previousRoute);
if (route is CupertinoPageRoute || route is MaterialPageRoute) {
_mRoutes.remove(route);
routeObserver();
}
}
@override
void didRemove(Route removedRoute, Route oldRoute) {
super.didRemove(removedRoute, oldRoute);
if (removedRoute is CupertinoPageRoute || removedRoute is MaterialPageRoute) {
_mRoutes.remove(removedRoute);
routeObserver();
}
}
void routeObserver() {
LogUtil.i(sName, '&&路由棧&&');
LogUtil.i(sName, _mRoutes);
LogUtil.i(sName, '&&當前路由&&');
LogUtil.i(sName, _mRoutes[_mRoutes.length - 1]);
// 當前頁面的navigator,用來路由跳轉
navigator = _mRoutes[_mRoutes.length - 1].navigator;
streamController.sink.add(_mRoutes);
}
}
複製程式碼
4. 如何使用
token失效跳轉
case 401:
ToastUtil.showRed('登入失效,請重新登陸');
UserDao.clearAll();
NavigatorManager.getInstance().pushNamedAndRemoveUntil(LoginPage.sName);
break;
複製程式碼
點選push推送跳轉
static jumpPage(String pageName, [WidgetBuilder builder]) {
String currentRouteName = NavigatorManager.getInstance().currentRoute.settings.name;
// 如果是未登入,不跳轉
if (NavigatorManager.getInstance().routes[0].settings.name != MainPage.sName) {
return;
}
// 如果已經是當前頁面就replace
if (currentRouteName == pageName) {
NavigatorManager.getInstance().pushReplacementNamed(pageName, builder);
} else {
NavigatorManager.getInstance().pushNamed(pageName, builder);
}
}
複製程式碼
監聽路由改變狀態列顏色
class StatusBarUtil {
static List<String> lightRouteNameList = [
TaskhallPage.sName,
//...
];
static List darkRoutNameList = [
SplashPage.sName,
LoginPage.sName,
MainPage.sName,
//...
];
static init() {
NavigatorManager.getInstance().streamController.stream.listen((state) {
setupStatusBar(state[state.length - 1]);
})
}
setupStatusBar(Route currentRoute) {
if (lightRouteNameList.contains(currentRoute.settings.name)) {
setLight();
} else if (darkRoutNameList.contains(currentRoute.settings.name)) {
setDart();
}
}
}
複製程式碼