如何仅在请求耗时超过200ms时弹出Dialog?
实现延迟显示的注销加载Dialog
你可以通过定时器+异步请求监听的方式实现需求,核心逻辑是:启动200ms定时器,若请求超时未完成则显示Dialog;请求完成后(无论成功/失败),立即取消定时器、关闭已显示的Dialog,最后跳转登录页。
下面提供两种适配你技术栈的实现方案:
方案一:结合Bloc状态监听(适配现有代码)
假设你的AuthBloc会在注销请求完成时发出LogoutSuccess或LogoutFailure状态,修改点击事件逻辑如下:
onTap: () { final authBloc = context.read<AuthBloc>(); Timer? loadingTimer; BuildContext? dialogContext; StreamSubscription? stateSubscription; // 启动200ms定时器,超时则显示加载Dialog loadingTimer = Timer(const Duration(milliseconds: 200), () { showDialog( context: context, barrierDismissible: false, // 禁止手动关闭 builder: (ctx) { dialogContext = ctx; return const AlertDialog( content: Row( children: [ CircularProgressIndicator(), SizedBox(width: 16), Text("正在注销..."), ], ), ); }, ); }); // 监听Bloc状态,处理请求完成后的逻辑 stateSubscription = authBloc.stream.listen((state) { if (state is LogoutSuccess || state is LogoutFailure) { // 取消定时器,避免Dialog后续弹出 loadingTimer?.cancel(); // 关闭已显示的Dialog if (dialogContext != null) { Navigator.of(dialogContext!).pop(); } // 跳转登录页 Navigator.of(context).push(MaterialPageRoute(builder: (context) => const LoginPage())); // 取消状态订阅,防止内存泄漏 stateSubscription?.cancel(); } }); // 发起注销请求 authBloc.add(LogoutRequested()); },
同时确保AuthBloc中LogoutRequested事件的处理为异步,并发出对应状态:
class AuthBloc extends Bloc<AuthEvent, AuthState> { final Dio dio; AuthBloc(this.dio) : super(AuthInitial()) { on<LogoutRequested>((event, emit) async { try { // 执行Dio注销请求 await dio.post('/logout'); emit(LogoutSuccess()); } catch (e) { emit(LogoutFailure(error: e.toString())); } }); } }
方案二:直接在点击事件中处理Dio请求(更轻量)
若不想依赖Bloc状态,可直接在点击事件中调用Dio接口,逻辑更直观:
onTap: () async { Timer? loadingTimer; BuildContext? dialogContext; // 启动200ms延迟定时器 loadingTimer = Timer(const Duration(milliseconds: 200), () { showDialog( context: context, barrierDismissible: false, builder: (ctx) { dialogContext = ctx; return const AlertDialog( content: Row( children: [ CircularProgressIndicator(), SizedBox(width: 16), Text("正在注销..."), ], ), ); }, ); }); try { // 执行Dio注销请求 await dio.post('/logout'); } catch (e) { // 可在此添加错误提示,比如SnackBar // ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("注销失败: $e"))); } finally { // 请求完成后,无论成功失败都清理资源 loadingTimer?.cancel(); if (dialogContext != null) { Navigator.of(dialogContext!).pop(); } // 跳转登录页 Navigator.of(context).push(MaterialPageRoute(builder: (context) => const LoginPage())); } },
关键注意事项
- 定时器清理:请求完成后必须取消定时器,避免请求快速完成后Dialog仍弹出
- Dialog关闭:保存Dialog的上下文用于后续关闭,因为
showDialog会创建新的上下文 - 内存泄漏防范:Bloc状态订阅要及时取消,避免组件销毁后仍存在监听
- 用户体验:设置
barrierDismissible: false防止用户在注销过程中手动关闭Dialog
内容的提问来源于stack exchange,提问作者Karol Wiśniewski
相关产品推荐
相关产品推荐

