#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
обратного вызова я выполняю проверку, и если это не удается, я возвращаю aDialog
с кнопкой, с которойonPressed
я отправляю событие. Извините, я не понял, что какой-то пример кода отсутствует, спасибо, что заставил меня заметить это.
Ответ №1:
Вы используете неправильный контекст при добавлении события. При отображении диалогового окна виджет будет помещен в оверлей, который находится над поставщиком блока, поэтому, используя контекст диалогового окна, вы не сможете найти блок, поскольку над ним нет поставщика.
Чтобы исправить это, назовите контекст диалога чем-то другим (т.е. dialogContext ) таким образом, чтобы при выполнении BlocProvider.of(context)
контекст ссылался на контекст виджета, отображающего диалоговое окно, а не на контекст самого диалога.
Комментарии:
1. Абсолютно! Вы действительно правы.. Я сам этого не видел. Большое вам спасибо и добро пожаловать в StackOverflow. Хорошее начало . Приветствия.