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

Flutter bloc - emit was called after an event handler completed normally

我在 user_bloc.dart 中有此代码:

UserBloc(this._userRepository, UserState userState) : super(userState) {
    on<RegisterUser>(_onRegisterUser);
  }

void _onRegisterUser(RegisterUser event, Emitter<UserState> emit) async {
    emit(UserRegistering());
    try {
      // detect when the user is registered
      FirebaseAuth.instance.authStateChanges().listen((User? user) async {
        if (state is UserRegistering && user != null) {
          // save user to db
          try {
            await _userRepository.addUpdateUserInfo(user.uid, userInfo);
          } catch (e) {
            emit(UserRegisteringError("Could not save user"));
          }
          emit(UserLoggedIn(user));
        }
      });
       ... call FirebaseAuth.instance.createUserWithEmailAndPassword

这是_userRepository.addUpdateUserInfo:

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

调用 emit(UserLoggedIn(user)); 时出现此错误:

Error: Assertion failed:
..\…\src\emitter.dart:114
!_isCompleted
"\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"
    at Object.throw_ [as throw] (http://localhost:58334/dart_sdk.js:5067:11)
    at Object.assertFailed (http://localhost:58334/dart_sdk.js:4992:15)
at _Emitter.new.call (http://localhost:58334/packages/bloc/src/transition.dart.lib.js:765:40)
at user_bloc.UserBloc.new.<anonymous> (http://localhost:58334/packages/soli/blocs/bloc/user_bloc.dart.lib.js:90:20)
    at Generator.next (<anonymous>)
    at http://localhost:58334/dart_sdk.js:40571:33
    at _RootZone.runUnary (http://localhost:58334/dart_sdk.js:40441:59)
    at _FutureListener.thenAwait.handleValue (http://localhost:58334/dart_sdk.js:35363:29)
    at handleValueCallback (http://localhost:58334/dart_sdk.js:35931:49)
    at Function._propagateToListeners (http://localhost:58334/dart_sdk.js:35969:17)
    at _Future.new.[_completeWithValue] (http://localhost:58334/dart_sdk.js:35817:23)
    at async._AsyncCallbackEntry.new.callback (http://localhost:58334/dart_sdk.js:35838:35)
    at Object._microtaskLoop (http://localhost:58334/dart_sdk.js:40708:13)
    at _startMicrotaskLoop (http://localhost:58334/dart_sdk.js:40714:13)
    at http://localhost:58334/dart_sdk.js:36191:9
[2022-02-17T03:47:00.057Z]  @firebase/firestore:

出现错误后,如果我用不同的用户重试,一切正常。

这是预期的结果,因为 _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
  }

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

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)));
}

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

Here 是来自 GitHub.

上的 bloc 存储库的示例