Flutter Bloc调用API时事件未注册处理程序错误排查
调用FetchUserDetails事件时提示未注册处理程序的问题排查与解决
错误日志
W/WindowOnBackDispatcher(10169): 应用未启用OnBackInvokedCallback。 W/WindowOnBackDispatcher(10169): 请在应用清单中设置'android:enableOnBackInvokedCallback="true"'。 E/flutter (10169): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] 未处理的异常:Bad state: add(FetchUserDetails) 被调用时未注册事件处理程序。 E/flutter (10169): 请确保通过on((event, emit) {...})注册处理程序 E/flutter (10169): #0 Bloc.add. (package:bloc/src/bloc.dart:91:9) E/flutter (10169): #1 Bloc.add (package:bloc/src/bloc.dart:97:6) E/flutter (10169): #2 _DashboardPageState._loadUsernameAndPin (package:mobile_banking/views/dashboard_page.dart:108:39)
相关代码实现
UserDetails 实体类
class UserDetails { final String username; final String password; final String deviceId; // Add required fields final String ipAddress; final String appVersion; final String platform; UserDetails({ required this.username, required this.password, required this.deviceId, required this.ipAddress, required this.appVersion, required this.platform, }); factory UserDetails.fromJson(Map<String, dynamic> json) { final data = json['data']; print(data); return UserDetails( username: json['username'] ?? '', password: json['password'] ?? '', deviceId: json['deviceId'] ?? '', ipAddress: json['ipAddress'] ?? '', appVersion: json['appVersion'] ?? '', platform: json['platform'] ?? '', ); } Map<String, dynamic> toJson() { return { 'username': username, 'password': password, 'deviceId': deviceId, 'ipAddress': ipAddress, 'appVersion': appVersion, 'platform': platform, }; } @override String toString() { return 'UserDetails{username: $username, deviceId: $deviceId, ipAddress: $ipAddress, appVersion: $appVersion, platform: $platform}'; } }
UserDetailsRepository
class UserDetailsRepository { final String endpoint = 'url'; late HttpService httpService; Future<UserDetails> getUserDetails(UserDetails userDetails) async { try { print('Requesting user details with: ${userDetails.toJson()}'); final response = await httpService.request( method: HttpMethod.post, url: '$endpoint/api/iBank/UserAuth', body: userDetails.toJson(), ); print("Response body: $response"); print("Return response body: ${UserDetails.fromJson(response)}"); return UserDetails.fromJson(response); } catch (e) { throw Exception('Failed to get user details'); } } }
状态类
abstract class UserDetailsState extends Equatable { @override List<Object?> get props => []; } class UserDetailsInitial extends UserDetailsState {} class UserDetailsLoading extends UserDetailsState {} class UserDetailsLoaded extends UserDetailsState { final Map<String, dynamic> userDetails; UserDetailsLoaded(this.userDetails); @override List<Object?> get props => [userDetails]; } class UserDetailsError extends UserDetailsState { final String message; UserDetailsError(this.message); @override List<Object?> get props => [message]; }
UserDetailsBloc(原错误实现)
class UserDetailsBloc extends Bloc<UserDetailsEvent, UserDetailsState> { UserDetailsBloc() : super(UserDetailsInitial()); Stream<UserDetailsState> mapEventtoState(UserDetailsEvent event) async* { if (event is FetchUserDetails) { yield UserDetailsLoading(); try { final fetchedDetails = await fetchUserDetailsFromStorage(event); print('user details fetched: $fetchedDetails'); yield UserDetailsLoaded(fetchedDetails.toJson()); } catch (e) { yield UserDetailsError('Failed to fetch user details: $e'); } } } Future<UserDetails> fetchUserDetailsFromStorage( FetchUserDetails event) async { // Implement the logic to fetch user details from secure storage or an API // Return the fetched details const storage = FlutterSecureStorage(); try { String? storedUsername = await storage.read(key: 'userName'); String? storedPassword = await storage.read(key: 'pin'); if (storedUsername != null && storedPassword != null) { // Create a UserDetails instance with the retrieved credentials UserDetails userDetails = UserDetails( username: storedUsername, password: storedPassword, deviceId: event.deviceId, ipAddress: event.ipAddress, appVersion: event.appVersion, platform: event.platform, ); // Fetch the user details from the repository UserDetails fetchedUserDetails = await UserDetailsRepository().getUserDetails(userDetails); return fetchedUserDetails; } else { throw Exception('Stored credentials not found'); } } catch (e) { throw Exception('Failed to fetch user details: $e'); } } }
问题原因
- 事件处理方法名拼写错误:原代码中
mapEventtoState的驼峰写法错误,正确应为mapEventToState(第二个T大写),且未添加@override注解,导致Bloc无法识别该事件处理方法。 - Bloc版本API不兼容:如果使用的是新版本Bloc(v8+),官方已废弃
mapEventToState,改用on<Event>的方式注册事件处理逻辑,原写法无法被新版本Bloc识别。
解决方案
方案1:修复mapEventToState写法(兼容旧版本Bloc)
修改UserDetailsBloc代码,修正方法名并添加@override:
class UserDetailsBloc extends Bloc<UserDetailsEvent, UserDetailsState> { UserDetailsBloc() : super(UserDetailsInitial()); @override // 添加override注解 Stream<UserDetailsState> mapEventToState(UserDetailsEvent event) async* { // 修正方法名 if (event is FetchUserDetails) { yield UserDetailsLoading(); try { final fetchedDetails = await fetchUserDetailsFromStorage(event); print('user details fetched: $fetchedDetails'); yield UserDetailsLoaded(fetchedDetails.toJson()); } catch (e) { yield UserDetailsError('Failed to fetch user details: $e'); } } } // 其余代码保持不变... }
方案2:迁移到新版本Bloc的on注册方式(推荐)
新版本Bloc推荐使用on<Event>来绑定事件与处理逻辑,修改后代码如下:
class UserDetailsBloc extends Bloc<UserDetailsEvent, UserDetailsState> { UserDetailsBloc() : super(UserDetailsInitial()) { // 注册FetchUserDetails事件的处理逻辑 on<FetchUserDetails>((event, emit) async { emit(UserDetailsLoading()); try { final fetchedDetails = await fetchUserDetailsFromStorage(event); print('user details fetched: $fetchedDetails'); emit(UserDetailsLoaded(fetchedDetails.toJson())); } catch (e) { emit(UserDetailsError('Failed to fetch user details: $e')); } }); } // 其余代码保持不变... }
额外注意事项
- 确保
FetchUserDetails事件类已正确定义(需继承自UserDetailsEvent)。 UserDetailsRepository中的httpService未初始化,需在使用前完成初始化,否则会导致空指针异常。- 关于
WindowOnBackDispatcher的警告,可在AndroidManifest.xml中添加android:enableOnBackInvokedCallback="true"到<application>标签中消除。
内容的提问来源于stack exchange,提问作者Anne Nduati
相关产品推荐
相关产品推荐

