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

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

我正在实施 FirebaseAuth 验证 phone 调整本指南 https://medium.com/@tapanrgohil/firebase-phone-authentication-in-flutter-with-bloc-pattern-4ddc2d43d76c 因为我不想登录,而只是 link 现有用户的新 PhoneAuthentication,我是在指南中使用 AuthenticationBloc 而不是 Losing Bloc es。

我在 PaymentScreen 中开始 phone 验证过程,我尝试直接在 PaymentScreenMultiBlocProvider 中提供 AuthenticationBloc,想创建一个新的 AuthenticationBloc 但错误是一样的。

AuthenticationBloc 中,基本上内部 StreamController 负责所有 phone 验证事件。 PaymentScreenBlocListener 中的传入 States 只是反应弹出和显示对话框,就像 AutoRetrieveCodeTimeout 显示手动 otp 插入对话框、错误、错误的 otp 等等。 为了找出导致不良状态的原因,我首先注释掉了所有上下文弹出窗口以确保它是那个,然后我注释掉了流中的所有 .close()

这些是来自控制台的打印:

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

你能看出是什么关闭了这个集团吗?

非常感谢。

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

身份验证事件:

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

身份验证状态:

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

支付界面:


@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
                      }
                    }
                  }
                ),

...

启动phone验证的对话框:

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

经过几次试验,我发现主要问题是使用 AuthenticationBloc 所以我做了一个专用的 PhoneAuthenticationBloc,同时仍然使用 AuthenticationStateAuthenticationState 并采取在没有`StreamController 的情况下处理 event/state 路由。

我会把 类 留在这里,以便对其他人有所帮助。

新集团:

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

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

事件:

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

状态:

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

                    }
                  }
                }),