如何让Flutter Bloc Stream持续监听?登录状态二次更新无响应
问题描述
我开发了一个登录API,正在Flutter中使用flutter_bloc对接,已成功连接API且可正常运行。首次提交请求时状态可响应,结果能显示在界面上:例如输入错误格式的邮箱并提交,可看到验证错误;但再次发送相同请求时,API能接收请求,状态却未发生变化。
代码如下:
void _handleSubmit() { Map<String, dynamic> formData = fieldValuesNotifier.value; final authBloc= context.read<AuthBloc>(); authBloc.add( LoginUser( formData['email'], formData['password'] ) ); authBloc.stream.listen((state) { // this line of code works when i hit api first time but not for second time // if (state is UserLoggedIn) { AutoRouter.of(context).popAndPush(const Motivation()); } else if(state is ValidationErrors){ setState(() { for (int i = 0; i < formFields.length; i++) { String field = formFields[i]['field']; if (state.validationErrors.containsKey(field)) { formFields[i]['error'] = state.validationErrors[field][0].toString(); } } }); } }); }
我希望实现状态的响应式更新,使其能对操作做出持续反应。
解决方法
核心问题是每次点击提交都重复监听Bloc的stream,但未取消之前的监听,导致后续状态变化无法正确触发,还可能引发内存泄漏。以下是具体修复方案:
1. 改用BlocListener(推荐)
Flutter Bloc官方推荐用BlocListener组件统一监听状态变化,避免手动管理监听的重复问题:
@override Widget build(BuildContext context) { return BlocListener<AuthBloc, AuthState>( listener: (context, state) { if (state is UserLoggedIn) { AutoRouter.of(context).popAndPush(const Motivation()); } else if (state is ValidationErrors) { setState(() { for (int i = 0; i < formFields.length; i++) { String field = formFields[i]['field']; if (state.validationErrors.containsKey(field)) { formFields[i]['error'] = state.validationErrors[field][0].toString(); } } }); } }, child: // 你的表单UI组件 ); } // 提交方法简化为仅添加事件 void _handleSubmit() { Map<String, dynamic> formData = fieldValuesNotifier.value; final authBloc = context.read<AuthBloc>(); authBloc.add(LoginUser(formData['email'], formData['password'])); }
2. 手动管理监听生命周期(备选)
如果坚持在提交方法内监听,必须保存StreamSubscription并在合适时机取消:
StreamSubscription? _authSubscription; @override void dispose() { _authSubscription?.cancel(); super.dispose(); } void _handleSubmit() { Map<String, dynamic> formData = fieldValuesNotifier.value; final authBloc = context.read<AuthBloc>(); // 先取消之前的监听 _authSubscription?.cancel(); authBloc.add(LoginUser(formData['email'], formData['password'])); _authSubscription = authBloc.stream.listen((state) { if (state is UserLoggedIn) { AutoRouter.of(context).popAndPush(const Motivation()); _authSubscription?.cancel(); // 登录成功后可取消监听 } else if (state is ValidationErrors) { setState(() { for (int i = 0; i < formFields.length; i++) { String field = formFields[i]['field']; if (state.validationErrors.containsKey(field)) { formFields[i]['error'] = state.validationErrors[field][0].toString(); } } }); } }); }
3. 确保状态类不可变
检查AuthState子类(如ValidationErrors)是否为不可变类型,Bloc仅会在emit新状态实例时触发更新:
class ValidationErrors extends AuthState { final Map<String, List<String>> validationErrors; const ValidationErrors({required this.validationErrors}); @override String toString() => 'ValidationErrors(validationErrors: $validationErrors)'; @override bool operator ==(Object other) { if (identical(this, other)) return true; return other is ValidationErrors && mapEquals(other.validationErrors, validationErrors); } @override int get hashCode => validationErrors.hashCode; }
4. 检查Bloc事件处理逻辑
确保处理LoginUser事件时,每次都emit新状态实例,而非修改已有状态:
class AuthBloc extends Bloc<AuthEvent, AuthState> { AuthBloc() : super(AuthInitial()) { on<LoginUser>((event, emit) async { final errors = <String, List<String>>{}; // 执行邮箱、密码验证逻辑,填充errors if (errors.isNotEmpty) { emit(ValidationErrors(validationErrors: errors)); // 每次emit新实例 return; } // 登录成功逻辑 emit(UserLoggedIn()); }); } }
内容的提问来源于stack exchange,提问作者Harris
相关产品推荐
相关产品推荐

