简体   繁体   English

错误状态:调用来自 FirebaseAuth.veryfyPhone 的 phoneCodeAutoRetrievalTimeout 回调后无法添加新事件。 弗特

[英]Bad state: Cannot add new events after phoneCodeAutoRetrievalTimeout callback from FirebaseAuth.veryfyPhone gets called. Futter

I'm implementing FirebaseAuth verify phone adapting this guide https://medium.com/@tapanrgohil/firebase-phone-authentication-in-flutter-with-bloc-pattern-4ddc2d43d76c as I don't want to sign in but just link the new PhoneAuthentication to existing user and I'm using AuthenticationBloc instead of Losing Bloc es in the guide.我正在实施 FirebaseAuth 验证电话以适应本指南https://medium.com/@tapanrgohil/firebase-phone-authentication-in-flutter-with-bloc-pattern-4ddc2d43d76c因为我不想登录但只是链接现有用户的新 PhoneAuthentication 并且我在指南中使用AuthenticationBloc而不是 Losing Bloc es。

I start the phone verification process in PaymentScreen and I tried providing AuthenticationBloc directly in PaymentScreen 's MultiBlocProvider , thinking create a new AuthenticationBloc but the error is the same.我在PaymentScreen启动电话验证过程,并尝试直接在PaymentScreenMultiBlocProvider提供AuthenticationBloc ,想创建一个新的AuthenticationBloc但错误是相同的。

In AuthenticationBloc basically an internal StreamController takes care of all phone verification events.AuthenticationBloc基本上一个内部StreamController负责所有电话验证事件。 Incoming States in PaymentScreen 's BlocListener just react popping and showing dialogs as in case of AutoRetrieveCodeTimeout showing manual otp insert dialog, errors, wrong otp and so on.传入StatesPaymentScreenBlocListener刚反应过来弹出,并显示在的情况下,对话AutoRetrieveCodeTimeout显示手动OTP插入对话框,错误,错误的OTP等。 To find out what's causing the bad state I first commented out all context pops just to make sure it wast that, and then I commented out all .close() in the stream.为了找出导致错误状态的原因,我首先注释掉所有上下文弹出,以确保它是那个,然后我注释掉流中的所有.close()

These are the prints from console:这些是来自控制台的打印:

I/flutter ( 7710): VerifyPhoneNumberEvent received
I/flutter ( 7710): _mapVerifyPhoneNumberToState started
I/BiChannelGoogleApi( 7710): [FirebaseAuth: ] getGoogleApiForMethod() returned Gms: com.google.firebase.auth.api.internal.zzaq@7f6fccb
I/flutter ( 7710): _mapVerifyPhoneNumberToState PhoneCodeSent
I/flutter ( 7710): PhoneCodeSentEvent received
I/flutter ( 7710): _mapVerifyPhoneNumberToState PhoneCodeAutoRetrievalTimeout
I/flutter ( 7710): Bloc error is Bad state: Cannot add new events after calling close
I/flutter ( 7710): Bloc error is Bad state: Cannot add new events after calling close
I/flutter ( 7710): Bloc error is Bad state: Cannot add new events after calling close
I/flutter ( 7710): Bloc error is Bad state: Cannot add new events after calling close
I/flutter ( 7710): Bloc error is Bad state: Cannot add new events after calling close
I/flutter ( 7710): Bloc error is Bad state: Cannot add new events after calling close

Can you spot what is closing the bloc?你能发现是什么在关闭这个集团吗?

Many thanks.非常感谢。

AuthenticationBloc身份验证块

class AuthenticationBloc
    extends Bloc<AuthenticationEvent, AuthenticationState> {
  final UserRepository _userRepository;

  AuthenticationBloc({@required UserRepository userRepository})
      : assert(userRepository != null),
        _userRepository = userRepository;

  StreamSubscription subscription;
  String verificationId = "";


  @override
  AuthenticationState get initialState => Uninitialized();

  @override
  Stream<AuthenticationState> mapEventToState(
      AuthenticationEvent event) async* {
    if (event is StartApp) {
      yield* _startAppToState();
    }
    if (event is AppStarted) {
      yield* _mapAppStartedToState();
    } else if (event is LoggedIn) {
      yield* _mapLoggedInToState();
    } else if (event is LoggedOut) {
      yield* _mapLoggedOutToState();
    }
    // phone verification
    if (event is VerifyPhoneNumberEvent) {
      print('VerifyPhoneNumberEvent received');
      yield VerifyingState();
      subscription = _mapVerifyPhoneNumberToState(event.phoneNumber).listen((event) {
        add(event);
      });
    } else if (event is PhoneCodeSentEvent) {
      print('PhoneCodeSentEvent received');
      yield OtpSentState();
    } else if (event is VerificationCompletedEvent) {
      print('VerificationCompletedEvent received');
      yield VerificationCompleteState(firebaseUser: event.firebaseUser, isVerified: event.isVerified);
    } else if (event is VerificationExceptionEvent) {
      print('VerificationExceptionEvent received');
      yield VerificationExceptionState(message: event.message);
    } else if (event is VerifySmsCodeEvent) {
      print('VerifySmsCodeEvent received');
      yield VerifyingState();
      try {
        AuthResult result =
        await _userRepository.verifyAndLinkAuthCredentials(verificationId: verificationId, smsCode: event.smsCode);
        if (result.user != null) {
          yield VerificationCompleteState(firebaseUser: result.user, isVerified: true);
        } else {
          yield OtpExceptionState(message: "Invalid otp!",verificationId: verificationId);
        }
      } catch (e) {
        yield OtpExceptionState(message: "Invalid otp!", verificationId: verificationId);
        print(e);
      }
    } else if ( event is PhoneCodeAutoRetrievalTimeoutEvent){
      yield PhoneCodeAutoRetrievalTimeoutState(verificationId: event.verificationId);
    }
    
    if(event is SendVerificationCodeEvent) {
      yield*_mapVerificationCodeToState(event);
    }


  }


  Stream<AuthenticationEvent> _mapVerifyPhoneNumberToState(String phoneNumber) async* {
    print('_mapVerifyPhoneNumberToState started');
    StreamController<AuthenticationEvent> phoneVerificationStreamController = StreamController();
    final phoneVerificationCompleted = (AuthCredential authCredential) {
      print('_mapVerifyPhoneNumberToState PhoneVerificationCompleted');
//      _userRepository.getUser();
      _userRepository.getCurrentUser().catchError((onError) {
        print(onError);
      }).then((user) {
        phoneVerificationStreamController.add(VerificationCompletedEvent(firebaseUser: user, isVerified:  true));
//        phoneVerificationStreamController.close();
      });
    };
    final phoneVerificationFailed = (AuthException authException) {
      print('_mapVerifyPhoneNumberToState PhoneVerificationFailed');
      print(authException.message);
      phoneVerificationStreamController.add(VerificationExceptionEvent(onError.toString()));
//      phoneVerificationStreamController.close();
    };
    final phoneCodeSent = (String verificationId, [int forceResent]) {
      print('_mapVerifyPhoneNumberToState PhoneCodeSent');
      this.verificationId = verificationId;
      phoneVerificationStreamController.add(PhoneCodeSentEvent());
    };
     final phoneCodeAutoRetrievalTimeout = (String verificationId) {

       // after this print Bloc error is Bad state: Cannot add new events after calling close
      print('_mapVerifyPhoneNumberToState PhoneCodeAutoRetrievalTimeout');
      this.verificationId = verificationId;
//      phoneVerificationStreamController.close();
//      phoneVerificationStreamController.add(PhoneCodeAutoRetrievalTimeoutEvent(verificationId: verificationId));
    };

    await _userRepository.verifyPhone(
        phoneNumber: phoneNumber,
        timeOut: Duration(seconds: 0), // 0 triggers PhoneCodeAutoRetrievalTimeout immediately
        phoneVerificationFailed: phoneVerificationFailed,
        phoneVerificationCompleted: phoneVerificationCompleted,
        phoneCodeSent: phoneCodeSent,
        autoRetrievalTimeout: phoneCodeAutoRetrievalTimeout);

    yield* phoneVerificationStreamController.stream;
  }

  Stream<AuthenticationState> _startAppToState() async* {
    Timer(Duration(seconds: 5), () {
      add(AppStarted());
    });
  }

  Stream<AuthenticationState> _mapAppStartedToState() async* {
    try {
      final isSignedIn = await _userRepository.isSignedIn();
      if (isSignedIn) {
        final user = await _userRepository.getUser();
        yield Authenticated(user);
      } else {
        yield Unauthenticated();
      }
    } catch (_) {
      yield Unauthenticated();
    }
  }

  Stream<AuthenticationState> _mapLoggedInToState() async* {
    yield Authenticated(await _userRepository.getUser());
  }

  Stream<AuthenticationState> _mapLoggedOutToState() async* {
    yield Unauthenticated();
    _userRepository.signOut();
  }

  Stream<AuthenticationState> _mapVerificationCodeToState(SendVerificationCodeEvent event) async* {
    print('_mapVerificationCodeToState started');
    yield VerifyingState();
    try {
      AuthResult result =
      await _userRepository.verifyAndLinkAuthCredentials(verificationId: verificationId, smsCode: event.smsCode);
      if (result.user != null) {
        yield VerificationCompleteState(firebaseUser: result.user, isVerified: true);
      } else {
        yield OtpExceptionState(message: "Invalid otp!", verificationId: verificationId);
      }
    } catch (e) {
      yield OtpExceptionState(message: "Invalid otp!", verificationId: verificationId);
      print(e);
    }
  }
}

AuthenticationEvent:身份验证事件:

class VerifyPhoneNumberEvent extends AuthenticationEvent {
  final String phoneNumber;
  VerifyPhoneNumberEvent({this.phoneNumber});
}



class VerifySmsCodeEvent extends AuthenticationEvent {
  final String smsCode;
  VerifySmsCodeEvent({this.smsCode});
}



class PhoneCodeSentEvent extends AuthenticationEvent {}

class VerificationCompletedEvent extends AuthenticationEvent {
  final FirebaseUser firebaseUser;
  final bool isVerified;
  VerificationCompletedEvent({@required this.firebaseUser, @required this.isVerified});
  @override
  List<Object> get props => [firebaseUser,isVerified];
  @override
  String toString() => 'VerificationCompleteEvent{user:${firebaseUser.displayName}, isVerified: $isVerified}';
}

class VerificationExceptionEvent extends AuthenticationEvent {
  final String message;

  VerificationExceptionEvent(this.message);
}
class PhoneCodeAutoRetrievalTimeoutEvent extends AuthenticationEvent {
  final String verificationId;
  PhoneCodeAutoRetrievalTimeoutEvent({@required this.verificationId});
  @override
  List<Object> get props => [verificationId];
  @override 
  String toString() => 'PhoneCodeAutoRetrievalTimeoutEvent {verificationId: $verificationId}';
}

AuthenticationState :身份验证状态:

class OtpSentState extends AuthenticationState {}

class VerifyingState extends AuthenticationState {}

class OtpVerifiedState extends AuthenticationState {}

class PhoneCodeAutoRetrievalTimeoutState extends AuthenticationState {
  final String verificationId;

  PhoneCodeAutoRetrievalTimeoutState({@required this.verificationId});
  @override
  List<Object> get props => [verificationId];
  @override
  String toString() => 'PhoneCodeAutoRetrievalTimeoutState {verificationId: $verificationId}';
}

class VerificationCompleteState extends AuthenticationState {
  final FirebaseUser firebaseUser;

  final bool isVerified;
  VerificationCompleteState({@required this.firebaseUser, @required this.isVerified});

  FirebaseUser getUser(){
    return firebaseUser;
  }
  @override
  List<Object> get props => [firebaseUser, isVerified];

  @override
  String toString() => 'VerificationCompleteState{user:${firebaseUser.displayName}, isVerified: $isVerified}';

}

class VerificationExceptionState extends AuthenticationState {
  final String message;

  VerificationExceptionState({this.message});

  @override
  // TODO: implement props
  List<Object> get props => [message];
}

class OtpExceptionState extends AuthenticationState {
  final String message;
  final String verificationId;

  OtpExceptionState({@required this.message, @required this.verificationId});

  @override
  // TODO: implement props
  List<Object> get props => [message, verificationId];
}

PaymentScreen :付款屏幕:


@override
  Widget build(BuildContext context) {
    return MultiBlocProvider(
      providers: [
        BlocProvider<AuthenticationBloc>(
          create: (context) => AuthenticationBloc(userRepository: UserRepository()),
          lazy: false,
        ),
        BlocProvider<UserBloc>(
          create: (context) => UserBloc(),
          lazy: false,
        ),
        BlocProvider<BookingBloc>(
          create: (context) => BookingBloc(user: widget.user),
        ),
        BlocProvider<OrderBloc>(
          create: (context) => OrderBloc(user: widget.user),
        ),
        BlocProvider<PaymentBloc>(
          create: (context) => PaymentBloc(user: widget.user),
          lazy: false,
        ),
        BlocProvider<CartBloc>(
          create: (context) => CartBloc()..add(LoadCart()),
        ),
      ],
    child:
    BlocBuilder<PaymentBloc, PaymentState>(builder: (context, state) {
        if (state is InitialStatePayment) {
          return MultiBlocListener(
              listeners: [
                BlocListener<AuthenticationBloc, AuthenticationState>(
                  listener: (BuildContext context, AuthenticationState state){
                    // ain't no sunshine
                    if (state is VerificationExceptionState ) {
                      scaffoldKey.currentState.showSnackBar(SnackBar(
                          backgroundColor: Colors.redAccent,
                          content: Text(
                              AppLocalizations.instance
                                  .text('Phone verification error'),
                              style: TextStyle(color: Colors.white))));
                    }
                    //manually insert OTP
                    if (state is PhoneCodeAutoRetrievalTimeoutState) {


                      print('PhoneCodeAutoRetrievalTimeoutState');
//                      setState(() {
                        controller.text = null;
//                      });
//                      Navigator.of(context,rootNavigator: false).pop(context);
                      showDialog(
                          context: context,
                          barrierDismissible: false,
                          builder: (BuildContext context){
                            return VerifyOtpDialog(
                                controller: controller,
                                onPressed: (){
                                  if (controller.text.length == 6) {
//                                    Navigator.of(context,rootNavigator: false).pop(context);
                                    BlocProvider.of<AuthenticationBloc>(context).add(SendVerificationCodeEvent(verificationId: state.verificationId, smsCode: controller.text.replaceAll(' ', '')));
                                  } else {
                                    scaffoldKey.currentState.showSnackBar(SnackBar(
                                        backgroundColor: Colors.redAccent,
                                        content: Text(
                                            AppLocalizations.instance
                                                .text('Wrong code'),
                                            style: TextStyle(color: Colors.white))));
                                  }
                                }
                            );
                          }
                      );
                    }
                    // if at the first you don't succeed..
                    if (state is OtpExceptionState) {
//                      setState(() {
                        controller.text = null;
//                      });
//                      Navigator.of(context,rootNavigator: false).pop(context);
                      showDialog(
                          context: context,
                          barrierDismissible: false,
                          builder: (BuildContext context){
                            return VerifyOtpRetryDialog(
                                controller: controller,
                                onPressed: (){
                                  if (controller.text.length == 6) {
//                                    Navigator.of(context,rootNavigator: false).pop();
                                    BlocProvider.of<AuthenticationBloc>(context).add(SendVerificationCodeEvent(verificationId: state.verificationId, smsCode: controller.text.replaceAll(' ', '')));
                                  } else {
                                    scaffoldKey.currentState.showSnackBar(SnackBar(
                                        backgroundColor: Colors.redAccent,
                                        content: Text(
                                            AppLocalizations.instance
                                                .text('Wrong code'),
                                            style: TextStyle(color: Colors.white))));
                                  }
                                }
                            );
                          }
                      );
                    }
                    // kool and the gang
                    if (state is VerificationCompleteState) {
                      if (state.isVerified == true) {
//                        setState(() {
                          isVerified = state.isVerified;
//                        });
//                        Navigator.of(context,rootNavigator: false).pop(context);
                        showDialog(
                          context: context,
                          barrierDismissible: false,
                          builder: (BuildContext context){
                            return VerifiedPhoneConfirmationDialog();
                          }
                        );
                        Timer(Duration(milliseconds: 1200), (){
//                          Navigator.of(context,rootNavigator: false).pop();
                        });

                        // TODO: Save user isVerified to LocalDb and Firebase
                      }
                    }
                  }
                ),

...

the dialog that starts the phone verification:启动电话验证的对话框:

showDialog(

                                        context: context,
                                        barrierDismissible: false,
                                        builder: (BuildContext context){
                                          return SingleChildScrollView(
                                            child: ValidatePhoneDialog(
                                              controller: controller,
                                                onPressed: (){
                                                if (controller.text.length >= 9){
//                                                  Navigator.pop(context); 
                                                  showDialog(
                                                    context:context,
                                                    barrierDismissible: false,
                                                    builder: (BuildContext context){
                                                      return VerifyingDialog();
                                                    }
                                                  );
                                                  BlocProvider.of<AuthenticationBloc>(context).add(VerifyPhoneNumberEvent(phoneNumber: controller.text.replaceAll(' ', '')));
                                                } else {
                                                  scaffoldKey.currentState.showSnackBar(SnackBar(
                                                      backgroundColor: Colors.redAccent,
                                                      content: Text(
                                                          AppLocalizations.instance
                                                              .text('Wrong number'),
                                                          style: TextStyle(color: Colors.white))));
                                                }
                                                }
                                            ),
                                          );
                                        }
                                      );

After a few trials I Found out that the main problem was using the AuthenticationBloc so I made a dedicated PhoneAuthenticationBloc while still using AuthenticationState and AuthenticationState and take care of the event/state routing without a `StreamController.经过几次试验,我发现主要问题是使用AuthenticationBloc所以我制作了一个专用的PhoneAuthenticationBloc同时仍然使用AuthenticationStateAuthenticationState并在没有`StreamController 的情况下处理事件/状态路由。

I'll leave the classes here so to be helpful to others.我会把课程留在这里,以便对他人有所帮助。

New bloc:新区块:

class PhoneAuthenticationBloc
    extends Bloc<AuthenticationEvent, AuthenticationState> {
  final UserRepository _userRepository;


  PhoneAuthenticationBloc({@required UserRepository userRepository})
      : assert(userRepository != null),
        _userRepository = userRepository;

  String verificationId = "";


  @override
  AuthenticationState get initialState => Uninitialized();

  @override
  Stream<AuthenticationState> mapEventToState(
      AuthenticationEvent event) async* {
    // phone verification
    if (event is VerifyPhoneNumberEvent) {
      print('VerifyPhoneNumberEvent received');
      yield VerifyingState();
      yield* _mapVerifyPhoneNumberToState(event);
    }
    if (event is PhoneCodeSentEvent) {
      print('PhoneCodeSentEvent received');
      yield OtpSentState();
    }
    if (event is VerificationCompletedEvent) {
      print('VerificationCompletedEvent received');
      yield VerificationCompleteState(firebaseUser: event.firebaseUser, isVerified: event.isVerified);
    }
    if (event is VerificationExceptionEvent) {
      print('VerificationExceptionEvent received');
      yield VerificationExceptionState(message: event.message);
    }

    if ( event is PhoneCodeAutoRetrievalTimeoutEvent){
      yield PhoneCodeAutoRetrievalTimeoutState(verificationId: event.verificationId);
    }

    if(event is SendVerificationCodeEvent) {
      yield VerifyingState();
      yield*_mapVerificationCodeToState(event);
    }


  }

  Stream<AuthenticationState> _mapVerifyPhoneNumberToState(VerifyPhoneNumberEvent event) async* {
    print('_mapVerifyPhoneNumberToState V2 started');
    final phoneVerificationCompleted = (AuthCredential authCredential) {
      print('_mapVerifyPhoneNumberToState PhoneVerificationCompleted');
      _userRepository.getCurrentUser().catchError((onError) {
        print(onError);
      }).then((user) {
        add(VerificationCompletedEvent(firebaseUser: user, isVerified:  true));
      });
    };
    final phoneVerificationFailed = (AuthException authException) {
      print('_mapVerifyPhoneNumberToState PhoneVerificationFailed');
      print(authException.message);
      add(VerificationExceptionEvent(onError.toString()));
    };
    final phoneCodeSent = (String verificationId, [int forceResent]) {
      print('_mapVerifyPhoneNumberToState PhoneCodeSent');
      this.verificationId = verificationId;
      add(PhoneCodeSentEvent());
    };
    final phoneCodeAutoRetrievalTimeout = (String verificationId) {
      print('_mapVerifyPhoneNumberToState PhoneCodeAutoRetrievalTimeout');
      this.verificationId = verificationId;
      add(PhoneCodeAutoRetrievalTimeoutEvent(verificationId: verificationId));
    };

    await _userRepository.verifyPhone(
        phoneNumber: event.phoneNumber,
        timeOut: Duration(seconds: 0),
        phoneVerificationFailed: phoneVerificationFailed,
        phoneVerificationCompleted: phoneVerificationCompleted,
        phoneCodeSent: phoneCodeSent,
        autoRetrievalTimeout: phoneCodeAutoRetrievalTimeout);
  }


  Stream<AuthenticationState> _mapVerificationCodeToState(SendVerificationCodeEvent event) async* {
    print('_mapVerificationCodeToState started');
      AuthResult result = await _userRepository.verifyAndLinkAuthCredentials(verificationId: verificationId, smsCode: event.smsCode)
          .catchError((e){
            print('verifyAndLinkAuthCredentials error: $e');
      });
      print(result);

      if (result != null) {
        yield VerificationCompleteState(firebaseUser: result.user, isVerified: true);
      } else {
        yield OtpExceptionState(message: "Invalid otp!", verificationId: verificationId);
      }
  }
}

new UserRepository methods:新的 UserRepository 方法:

Future<void> verifyPhone(
      {@required String phoneNumber,
      @required Duration timeOut,
      @required PhoneVerificationFailed phoneVerificationFailed,
      @required PhoneVerificationCompleted phoneVerificationCompleted,
      @required PhoneCodeSent phoneCodeSent,
      @required PhoneCodeAutoRetrievalTimeout autoRetrievalTimeout}) async {
    _firebaseAuth.verifyPhoneNumber(
        phoneNumber: phoneNumber,
        timeout: timeOut,
        verificationCompleted: phoneVerificationCompleted,
        verificationFailed: phoneVerificationFailed,
        codeSent: phoneCodeSent,
        codeAutoRetrievalTimeout: autoRetrievalTimeout);
  }

  Future<AuthResult> verifyAndLinkAuthCredentials(
  {@required String verificationId, @required String smsCode}) async {
    AuthCredential authCredential = PhoneAuthProvider.getCredential(
        verificationId: verificationId, smsCode: smsCode);

//    return _firebaseAuth.signInWithCredential(authCredential);

    FirebaseUser user = await _firebaseAuth.currentUser();
    return user.linkWithCredential(authCredential).catchError((e) {
      print('UserRepository.verifyAndLinkAuthCredentials() error: $e');
//      return;
      });
  }

events:事件:

class VerifyPhoneNumberEvent extends AuthenticationEvent {
  final String phoneNumber;
  VerifyPhoneNumberEvent({this.phoneNumber});
}



class VerifySmsCodeEvent extends AuthenticationEvent {
  final String smsCode;
  VerifySmsCodeEvent({this.smsCode});
}



class PhoneCodeSentEvent extends AuthenticationEvent {}

class VerificationCompletedEvent extends AuthenticationEvent {
  final FirebaseUser firebaseUser;
  final bool isVerified;
  VerificationCompletedEvent({@required this.firebaseUser, @required this.isVerified});
  @override
  List<Object> get props => [firebaseUser,isVerified];
  @override
  String toString() => 'VerificationCompleteEvent{user:${firebaseUser.displayName}, isVerified: $isVerified}';
}

class VerificationExceptionEvent extends AuthenticationEvent {
  final String message;

  VerificationExceptionEvent(this.message);
}
class PhoneCodeAutoRetrievalTimeoutEvent extends AuthenticationEvent {
  final String verificationId;
  PhoneCodeAutoRetrievalTimeoutEvent({@required this.verificationId});
  @override
  List<Object> get props => [verificationId];
  @override 
  String toString() => 'PhoneCodeAutoRetrievalTimeoutEvent {verificationId: $verificationId}';
}

states:状态:

class OtpSentState extends AuthenticationState {}

class VerifyingState extends AuthenticationState {}

class OtpVerifiedState extends AuthenticationState {}

class PhoneCodeAutoRetrievalTimeoutState extends AuthenticationState {
  final String verificationId;

  PhoneCodeAutoRetrievalTimeoutState({@required this.verificationId});
  @override
  List<Object> get props => [verificationId];
  @override
  String toString() => 'PhoneCodeAutoRetrievalTimeoutState {verificationId: $verificationId}';
}

class VerificationCompleteState extends AuthenticationState {
  final FirebaseUser firebaseUser;

  final bool isVerified;
  VerificationCompleteState({@required this.firebaseUser, @required this.isVerified});

  FirebaseUser getUser(){
    return firebaseUser;
  }
  @override
  List<Object> get props => [firebaseUser, isVerified];

  @override
  String toString() => 'VerificationCompleteState{user:${firebaseUser.displayName}, isVerified: $isVerified}';

}

class VerificationExceptionState extends AuthenticationState {
  final String message;

  VerificationExceptionState({this.message});

  @override
  // TODO: implement props
  List<Object> get props => [message];
}

class OtpExceptionState extends AuthenticationState {
  final String message;
  final String verificationId;

  OtpExceptionState({@required this.message, @required this.verificationId});

  @override
  // TODO: implement props
  List<Object> get props => [message, verificationId];
}

UI:用户界面:

showDialog(
                                          context: context,
                                          barrierDismissible: false,
                                          builder:
                                              (BuildContext dialogContext) {
                                            return SingleChildScrollView(
                                              child: ValidatePhoneDialog(
                                                  controller: controller,
                                                  onPressed: () {
                                                    if (controller
                                                            .text.length >=
                                                        9) {
                                                      BlocProvider.of<
                                                                  PhoneAuthenticationBloc>(
                                                              context)
                                                          .add(VerifyPhoneNumberEvent(
                                                              phoneNumber:
                                                                  controller
                                                                      .text
                                                                      .replaceAll(
                                                                          ' ',
                                                                          '')));
                                                    } else {
                                                      scaffoldKey.currentState
                                                          .showSnackBar(SnackBar(
                                                              backgroundColor:
                                                                  Colors
                                                                      .redAccent,
                                                              content: Text(
                                                                  AppLocalizations
                                                                      .instance
                                                                      .text(
                                                                          'Wrong number'),
                                                                  style: TextStyle(
                                                                      color: Colors
                                                                          .white))));
                                                    }
                                                  }),
                                            );
                                          });

BlocListener:块监听器:

BlocListener<PhoneAuthenticationBloc, AuthenticationState>(
                    listener:
                        (BuildContext context, AuthenticationState state) {
                  if (state is VerifyingState) {
                    Navigator.of(context, rootNavigator: false).pop(context);
                    showDialog(
                        context: context,
                        barrierDismissible: false,
                        builder: (BuildContext context) {
                          return VerifyingDialog();
                        });
                  }
                  if (state is VerificationExceptionState) {
                    // ain't no sunshine
                    Navigator.of(context, rootNavigator: false).pop(context);
                    // Todo retry
                    showDialog(
                        context: context,
                        barrierDismissible: false,
                        builder: (BuildContext dialogContext) {
                          return SingleChildScrollView(
                            child: ValidatePhoneRetryDialog(
                                controller: controller,
                                onPressed: () {
                                  if (controller.text.length >= 9) {
                                    BlocProvider.of<PhoneAuthenticationBloc>(
                                            context)
                                        .add(VerifyPhoneNumberEvent(
                                            phoneNumber: controller.text
                                                .replaceAll(' ', '')));
//                                                    Navigator.pop(context);
                                    Navigator.of(context, rootNavigator: false)
                                        .pop();
                                    showDialog(
                                        context: context,
                                        barrierDismissible: false,
                                        builder: (BuildContext context) {
                                          return VerifyingDialog();
                                        });
                                  } else {
                                    scaffoldKey.currentState.showSnackBar(
                                        SnackBar(
                                            backgroundColor: Colors.redAccent,
                                            content: Text(
                                                AppLocalizations.instance
                                                    .text('Wrong number'),
                                                style: TextStyle(
                                                    color: Colors.white))));
                                  }
                                }),
                          );
                        });
//                        scaffoldKey.currentState.showSnackBar(SnackBar(
//                            backgroundColor: Colors.redAccent,
//                            content: Text(
//                                AppLocalizations.instance
//                                    .text('Phone verification error'),
//                                style: TextStyle(color: Colors.white))));
                  }

                  if (state is PhoneCodeAutoRetrievalTimeoutState) {
                    //manually insert OTP
                    print('PhoneCodeAutoRetrievalTimeoutState');
                    setState(() {
                      controller.text = '';
                    });
                    Navigator.of(context, rootNavigator: false).pop(context);
                    showDialog(
                        context: context,
                        barrierDismissible: false,
                        builder: (BuildContext dialogContext) {
                          return SingleChildScrollView(
                            child: VerifyOtpDialog(
                                controller: controller,
                                onPressed: () {
                                  if (controller.text.length == 6) {
                                    BlocProvider.of<PhoneAuthenticationBloc>(
                                            context)
                                        .add(SendVerificationCodeEvent(
                                            verificationId:
                                                state.verificationId,
                                            smsCode: controller.text
                                                .replaceAll(' ', '')));
                                  } else {
                                    scaffoldKey.currentState.showSnackBar(
                                        SnackBar(
                                            backgroundColor: Colors.redAccent,
                                            content: Text(
                                                AppLocalizations.instance
                                                    .text('Wrong code'),
                                                style: TextStyle(
                                                    color: Colors.white))));
                                  }
                                }),
                          );
                        });
                  }

                  if (state is OtpExceptionState) {
                    // if at the first you don't succeed..
                    Navigator.of(context, rootNavigator: false).pop(context);
                    showDialog(
                        context: context,
                        barrierDismissible: false,
                        builder: (BuildContext dialogContext) {
                          return SingleChildScrollView(
                            child: VerifyOtpRetryDialog(
                                controller: controller,
                                onPressed: () {
                                  if (controller.text.length == 6) {
                                    BlocProvider.of<PhoneAuthenticationBloc>(
                                            context)
                                        .add(SendVerificationCodeEvent(
                                            verificationId:
                                                state.verificationId,
                                            smsCode: controller.text
                                                .replaceAll(' ', '')));
                                  } else {
                                    scaffoldKey.currentState.showSnackBar(
                                        SnackBar(
                                            backgroundColor: Colors.redAccent,
                                            content: Text(
                                                AppLocalizations.instance
                                                    .text('Wrong code'),
                                                style: TextStyle(
                                                    color: Colors.white))));
                                  }
                                }),
                          );
                        });
                  }

                  if (state is VerificationCompleteState) {
                    // kool and the gang
                    if (state.isVerified == true) {
                      setState(() {
                        isVerified = state.isVerified;
                      });
                      Navigator.of(context, rootNavigator: false).pop(context);
                      showDialog(
                          context: context,
                          barrierDismissible: false,
                          builder: (BuildContext dialogContext) {
                            return VerifiedPhoneConfirmationDialog();
                          });
                      BlocProvider.of<UserBloc>(context)
                          .add(UserPhoneVerified(user: widget.user));
                      Timer(Duration(milliseconds: 1200), () {
                        Navigator.of(context, rootNavigator: false).pop();
                      });

                      // TODO: Save user isVerified to LocalDb and Firebase

                    }
                  }
                }),

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Flutter:来自后退导航的回调产生“错误的 state:调用关闭后无法添加新事件” - Flutter : Callback from back navigation produces “Bad state: Cannot add new events after calling close” StateError(错误状态:调用关闭后无法添加新事件) - StateError (Bad state: Cannot add new events after calling close) 错误:错误状态:调用关闭后无法添加新事件 - Error: Bad state: Cannot add new events after calling close flutter:未处理的异常:错误 state:调用关闭后无法添加新事件 - flutter: Unhandled Exception: Bad state: Cannot add new events after calling close 错误 State:调用关闭后无法添加新事件 - Flutter 模块化 - Bad State: Cannot add new events after calling close - Flutter Modular 未处理的异常:错误 state:调用关闭音频播放器后无法添加新事件 - Unhandled Exception: Bad state: Cannot add new events after calling close Audio player 未处理的异常:错误 state:调用关闭后无法添加新事件 - 屏幕之间 - Unhandled Exception: Bad state: Cannot add new events after calling close - Between screens Flutter:未处理的异常:错误状态:调用close后无法添加新事件(不一样的情况) - Flutter: Unhandled Exception: Bad state: Cannot add new events after calling close (NOT SAME CASE) 未处理的异常:错误 state:调用关闭后无法添加新事件? - Unhandled Exception: Bad state: Cannot add new events after calling close? 未处理的异常:错误 state:在使用 fluttter_google_places 调用关闭后无法添加新事件 - Unhandled Exception: Bad state: Cannot add new events after calling close, on using fluttter_google_places
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM