使用事件总线
1.创建事件总线
class EventBus {
//私有构造函数
EventBus._internal();
static EventBus? _instance;
static EventBus get instance => _getInstance();
static EventBus _getInstance() {
return _instance ??= EventBus._internal();
}
// 存储事件回调方法
final Map<String, Function> _events = {};
// 设置事件监听
void addListener(String eventKey, Function callback) {
_events[eventKey] = callback;
}
// 移除监听
void removeListener(String eventKey) {
_events.remove(eventKey);
}
// 提交事件
void commit(String eventKey) {
_events[eventKey]?.call();
}
}
class EventKeys {
static const String logout = "Logout";
}
2.监听登出事件
登录成功后,在主页面设置登出事件监听,事件响应后立即移除监听。
class TabPage extends StatefulWidget {
const TabPage({Key? key}) : super(key: key);
@override
State<TabPage> createState() => _TabPageState();
}
class _TabPageState extends State<TabPage> {
@override
void initState() {
super.initState();
EventBus.instance.addListener(EventKeys.logout, () {
// 移除事件监听
EventBus.instance.removeListener(EventKeys.logout);
// 跳转登录页面
Navigator.of(context).pushNamedAndRemoveUntil('/login', (route) => false);
});
}
@override
void dispose() {
// 移除事件监听
EventBus.instance.removeListener(EventKeys.logout);
super.dispose();
}
@override
Widget build(BuildContext context) {
return ...
}
}
3 网络请求拦截器中触发登出事件
final dio = Dio()..interceptors.add(ApiInterceptor());
class ApiInterceptor extends InterceptorsWrapper {
@override
void onError(DioError err, ErrorInterceptorHandler handler) {
super.onError(err, handler);
int? statusCode = err.response?.statusCode;
if (statusCode == 401) {
// 触发登出事件
EventBus.instance.commit(EventKeys.logout);
}
}
}
嗯嗯,感谢原文大佬鼎力相助,使用了以上方式,亲测好用,
另外,原文(Flutter 实现统一处理Token过期后跳转登录页面_xhu_ww的博客-CSDN博客_flutter token失效)中提到的第一种方式,可能会出现的问题“当同时存在多个网络请求时,拦截器中跳转登录页面的操作可能会同时调用几次,出现多次跳转登录页面的情况。
解决方式如下:
页面跳转使用GetX方式
// 引入
import 'package:get/get.dart' as GetResponse;
// 在页面跳转中使用
GetResponse.Get.toNamed("/login");
注意:此方法需在 main.dart 中修改
MaterialApp 修改为=> GetMaterialApp
当然用了 GetX 也就无需使用 事件总线 和 全局GlobalKey 啦