Flutter bloc - 在事件处理程序正常完成后调用了emit

Ron*_*ler 3 async-await dart flutter flutter-bloc

我在 user_bloc.dart 中有这段代码:

\n
UserBloc(this._userRepository, UserState userState) : super(userState) {\n    on<RegisterUser>(_onRegisterUser);\n  }\n\nvoid _onRegisterUser(RegisterUser event, Emitter<UserState> emit) async {\n    emit(UserRegistering());\n    try {\n      // detect when the user is registered\n      FirebaseAuth.instance.authStateChanges().listen((User? user) async {\n        if (state is UserRegistering && user != null) {\n          // save user to db\n          try {\n            await _userRepository.addUpdateUserInfo(user.uid, userInfo);\n          } catch (e) {\n            emit(UserRegisteringError("Could not save user"));\n          }\n          emit(UserLoggedIn(user));\n        }\n      });\n       ... call FirebaseAuth.instance.createUserWithEmailAndPassword\n
Run Code Online (Sandbox Code Playgroud)\n

这是 _userRepository.addUpdateUserInfo:

\n
Future<void> addUpdateUserInfo(String userId, UserInfo userInfo) async {\n    try {\n      var doc = usersInfo.doc(userId);\n      await doc.set(\n        {\n          'first_name': userInfo.firstName,\n          'last_name': userInfo.lastName,\n          'email': userInfo.email\n        },\n        SetOptions(merge: true),\n      );\n    } catch (e) {\n      print("Failed to add user: $e");\n      throw Exception("Failed to add user");\n    }\n  }\n
Run Code Online (Sandbox Code Playgroud)\n

emit(UserLoggedIn(user));被调用时我收到此错误:

\n
Error: Assertion failed:\n..\\\xe2\x80\xa6\\src\\emitter.dart:114\n!_isCompleted\n"\\n\\n\\nemit was called after an event handler completed normally.\\nThis is usually due to an unawaited future in an event handler.\\nPlease make sure to await all asynchronous operations with event handlers\\nand use emit.isDone after asynchronous operations before calling emit() to\\nensure the event handler has not completed.\\n\\n  **BAD**\\n  on<Event>((event, emit) {\\n    future.whenComplete(() => emit(...));\\n  });\\n\\n  **GOOD**\\n  on<Event>((event, emit) async {\\n    await future.whenComplete(() => emit(...));\\n  });\\n"\n    at Object.throw_ [as throw] (http://localhost:58334/dart_sdk.js:5067:11)\n    at Object.assertFailed (http://localhost:58334/dart_sdk.js:4992:15)\nat _Emitter.new.call (http://localhost:58334/packages/bloc/src/transition.dart.lib.js:765:40)\nat user_bloc.UserBloc.new.<anonymous> (http://localhost:58334/packages/soli/blocs/bloc/user_bloc.dart.lib.js:90:20)\n    at Generator.next (<anonymous>)\n    at http://localhost:58334/dart_sdk.js:40571:33\n    at _RootZone.runUnary (http://localhost:58334/dart_sdk.js:40441:59)\n    at _FutureListener.thenAwait.handleValue (http://localhost:58334/dart_sdk.js:35363:29)\n    at handleValueCallback (http://localhost:58334/dart_sdk.js:35931:49)\n    at Function._propagateToListeners (http://localhost:58334/dart_sdk.js:35969:17)\n    at _Future.new.[_completeWithValue] (http://localhost:58334/dart_sdk.js:35817:23)\n    at async._AsyncCallbackEntry.new.callback (http://localhost:58334/dart_sdk.js:35838:35)\n    at Object._microtaskLoop (http://localhost:58334/dart_sdk.js:40708:13)\n    at _startMicrotaskLoop (http://localhost:58334/dart_sdk.js:40714:13)\n    at http://localhost:58334/dart_sdk.js:36191:9\n[2022-02-17T03:47:00.057Z]  @firebase/firestore:\n
Run Code Online (Sandbox Code Playgroud)\n

收到错误后,如果我用其他用户重试,则可以正常工作。

\n

mko*_*lys 5

这是预期的结果,因为该_onRegisterUser方法已经完成执行,但内部的代码FirebaseAuth.instance.authStateChanges().listen(...)随后尝试发出状态更改。

在这种情况下,您可以做什么,而不是在 FirebaseAuth 侦听器内发出新状态,您应该向 BLoC 添加新事件:

if (state is UserRegistering && user != null) {
  // save user to db
  try {
    await _userRepository.addUpdateUserInfo(user.uid, userInfo);
  } catch (e) {
    add(RegistrationErrorEvent()); // Create this event
  }
    add(UserLoggedInEvent(user: user)); // Create this event
  }
Run Code Online (Sandbox Code Playgroud)

然后,您应该注册这些事件并处理逻辑:

UserBloc(this._userRepository, UserState userState) : super(userState) {
  on<RegisterUser>(_onRegisterUser);
  on<RegistrationErrorEvent>((event, emit) => emit(UserRegisteringError("Could not save user")));
  on<UserLoggedInEvent>((event, emit) => emit(UserLoggedIn(event.user)));
}
Run Code Online (Sandbox Code Playgroud)

另外,由于您正在使用流和listen方法,请考虑使用,StreamSubscription以便您可以在侦听状态更改后清理资源。

以下是 GitHub 上 bloc 存储库的示例。