1. 路由选择
Flutter路由跳转方式有如下两种:
-
基本路由
Navigator.of(context).push( MaterialPageRoute( builder: (context)=>Detail() ) );
-
命名式路由
Navigator.pushNamed(context, "Detail")
我选择了后者,主要原因是符合我之前做RN时的路由习惯,并无其他深层的原因。另外在此不再赘述两种路由的具体使用,后续代码默认大家都熟悉这一块。
2. 需要解决的问题
-
不管使用哪种路由方式都需要获取当前路由上下文
context
,但并非所有路由跳转都可以直接获取到当前路由上下文ROUTE文件 1️⃣ 2️⃣ 组合技解决该问题
-
在有Tab路由情况下,如何返回指定Tab页
曾经天真的认为会有现成的API,但一顿尝试以后并没有找到,无奈只能借用通知机制处理,详情见ROUTE文件3️⃣
-
flutter没有提供获取路由栈的方法,因此只能自己维护(见ROUTE文件4️⃣),但又面临如下两个问题:
- 如何在触发iOS侧滑或Android物理返回键时将当前路由出栈
- flutter中Modal是通过路由的方式弹出的,在维护路由栈时又是如何排除Modal的出栈与入栈呢
如果你看了ROUTE中每个跳转维护路由栈的逻辑,你会发现一个奇怪的现象:所有的pop方法里我都没有对路由栈做出栈操作。这是因为我在MaterialApp 增加了一个navigatorObservers监听,即GlobalNavigatorObserver(源码在最后)。路由中的任何pop操作都会触发该监听中的didPop方法,包括
iOS侧滑
、Android物理返回键
、Modal的pop
。因此pop的路由栈维护就交给了GlobalNavigatorObserver。
2. 源码分析
ROUTE.dart
// 1️⃣ 将该值赋给MaterialApp的navigatorKey属性
GlobalKey<NavigatorState> NavigatorKey = GlobalKey();
// 2️⃣ 获取当前路由对象,可以通过Navigtaion.context获取当前路由上下文
var Navigation = NavigatorKey.currentState;
class ROUTE {
// 4️⃣ 手动维护的路由栈,每次路由操作都会对该数组进行出栈入栈操作
static List<String> history = [];
static Future push(name, [params]) {
history.add(name);
print('当前路由栈 $history');
return Navigation.pushNamed(name, arguments: params);
}
static void pushAnomymous(name, [params]) {
// 是否登录的判断
if (user?.uid == null) {
push("LoginPage",params);
} else {
push(name, params);
}
}
// Modal的pop,再次统一管理,另外也必须使用Navigation下的context,否则会影响路由栈
static modalPop() {
Navigator.of(Navigation.context).pop();
}
// value是回调的值
static void pop([value]) {
Navigation.pop(value);
}
// pop到指定页面
static void popTo(name) {
Navigation.popUntil(ModalRoute.withName(name));
}
static void goToTab(tab) async {
popTo('/');
history = [name];
// 3️⃣ 借用通知,告知Tab页要切换的tab项。EventBus是自己网上找的通知封装,如果需要可以评论或私信
EventBus().emit('PopTab', {'tab': tab});
}
// 当前路由栈是否可pop
static bool canPop() {
return Navigation.canPop();
}
// 会先调用canPop,若返回false则无效果
static void maybePop() {
Navigation.maybePop();
}
static Future replace(name, [params]) async {
var arg = Map();
arg.addAll(params ?? {});
arg['replace'] = true;
if (history.isNotEmpty) {
history.removeLast();
}
history.add(name);
await Navigation.pushReplacementNamed(name, arguments: arg);
}
// 先执行pop再执行push
static void popAndPush(name) {
if (history.isNotEmpty) {
history.removeLast();
}
history.add(name);
Navigation.popAndPushNamed(name);
}
// push并重置路由栈,可以通过第二个回调来保留某些路由栈
static Future pushReset(name) {
history = [name];
return Navigation.pushNamedAndRemoveUntil(name, (route) => false);
}
}
GlobalNavigatorObserver.dart
import 'package:app/functions/route/route.dart';
import 'package:flutter/material.dart';
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
class GlobalNavigatorObserver extends NavigatorObserver {
@override
void didPop(Route route, Route previousRoute) {
super.didPop(route, previousRoute);
if (route is ModalBottomSheetRoute || route is DialogRoute) return;
ROUTE.restoreGlobarBar();
ROUTE.history.removeLast();
}
}