BlocProvider.of() вызывается с контекстом, который не содержит блока типа PhoneAuthenticationBloc . Флаттер

#flutter #bloc

#флаттер #блок

Вопрос:

Я создаю блоки в a MultiBlocProvider , его дочерним элементом является a BlocBuilder , который возвращает a MultiBlocListener , но при отправке события

BlocProvider.of<PhoneAuthenticationBloc>(context).add(VerifyPhoneNumberEvent(phoneNumber: controller.text.replaceAll(' ', '')));

Я получаю BlocProvider.of() called with a context that does not contain a Bloc of type PhoneAuthenticationBloc , в то время как другие блоки работают нормально.

Можете ли вы определить, что не так PhoneAuthenticationBloc() ?

  @override
  Widget build(BuildContext context) {
    return MultiBlocProvider(
      providers: [
        BlocProvider<PhoneAuthenticationBloc>(
          create: (context) => PhoneAuthenticationBloc(userRepository: UserRepository()),
        ),
//        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<PhoneAuthenticationBloc, AuthenticationState>(
                    listener: (BuildContext context, AuthenticationState state){
...


FlatButton.icon(
                                    color: Colors.orange,
                                    onPressed: () {
                                      print('pay pressed');
                                      print(
                                          'bookingStart is ${widget.bookingStart}, selected shop is ${widget.selectedShop}');
                                     if (isVerified == true){
                                       ...
                                     }
                                      else{
                                        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<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))));
                                                  }
                                                  }
                                              ),
                                            );
                                          }
                                        );
                                     }



                                    },
                                    icon: Icon(
                                      Icons.payment,
                                      color: Colors.white,
                                    ),
                                    label: Text(
                                      AppLocalizations.instance.text('Pay'),
                                      style: TextStyle(
                                          color: Colors.white, fontSize: 20),
                                    )),


  
 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* _mapVerifyPhoneNumberToState(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<AuthenticationState> _mapVerifyPhoneNumberToState(VerifyPhoneNumberEvent event) async* {
    print('_mapVerifyPhoneNumberToState V2 started');
    yield VerifyingState();



    final phoneVerificationCompleted = (AuthCredential authCredential) {
      print('_mapVerifyPhoneNumberToState PhoneVerificationCompleted');
//      _userRepository.getUser();
      _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) {

      // after this print Bloc error is Bad state: Cannot add new events after calling close
      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');
    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);
    }
  }
}
  

Комментарии:

1. Куда вы вызываете BlocProvider.of<PhoneAuthenticationBloc>

2. @PietervanLoon В кнопке onPressed обратного вызова я выполняю проверку, и если это не удается, я возвращаю a Dialog с кнопкой, с которой onPressed я отправляю событие. Извините, я не понял, что какой-то пример кода отсутствует, спасибо, что заставил меня заметить это.

Ответ №1:

Вы используете неправильный контекст при добавлении события. При отображении диалогового окна виджет будет помещен в оверлей, который находится над поставщиком блока, поэтому, используя контекст диалогового окна, вы не сможете найти блок, поскольку над ним нет поставщика.

Чтобы исправить это, назовите контекст диалога чем-то другим (т.е. dialogContext ) таким образом, чтобы при выполнении BlocProvider.of(context) контекст ссылался на контекст виджета, отображающего диалоговое окно, а не на контекст самого диалога.

Комментарии:

1. Абсолютно! Вы действительно правы.. Я сам этого не видел. Большое вам спасибо и добро пожаловать в StackOverflow. Хорошее начало . Приветствия.