Плохое состояние: невозможно добавить новые события после обратного вызова phoneCodeAutoRetrievalTimeout из FirebaseAuth.veryfyPhone. Futter

Я использую FirebaseAuth для проверки телефона, адаптируя это руководство https://medium.com/@tapanrgohil/firebase-phone-authentication-in-flutter-with-bloc-pattern-4ddc2d43d76c, так как я не хочу входить в систему, а просто привяжу новую аутентификацию PhoneAuthentication к существующей user, и я использую AuthenticationBloc вместо Losing Bloc в руководстве.

Я начинаю процесс проверки телефона в PaymentScreen и пытался указать AuthenticationBloc непосредственно в PaymentScreen MultiBlocProvider, думая создать новый AuthenticationBloc, но ошибка та же.

В AuthenticationBloc в основном внутренняя StreamController заботится обо всех событиях проверки телефона. Входящий States в PaymentScreen BlocListener просто реагирует, выскакивая и показывая диалоги, как в случае 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);
    }
  }
}

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

Платежный экран:


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

...

диалоговое окно, запускающее проверку телефона:

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

person Vincenzo    schedule 24.10.2020    source источник


Ответы (1)


После нескольких испытаний я обнаружил, что основная проблема заключалась в использовании AuthenticationBloc, поэтому я сделал специальный PhoneAuthenticationBloc, все еще используя AuthenticationState и AuthenticationState, и позаботился о маршрутизации событий / состояний без StreamController.

Я оставлю занятия здесь, чтобы быть полезными другим.

Новый блок:

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

                    }
                  }
                }),
person Vincenzo    schedule 24.10.2020