felangel / bloc

A predictable state management library that helps implement the BLoC design pattern
https://bloclibrary.dev
MIT License
11.71k stars 3.38k forks source link

bloc listener is not listening to any state #3916

Closed Div-47 closed 1 year ago

Div-47 commented 1 year ago

The bloc listener is not listening to any state after firebase phone auth recaptcha verification. i think its happening while navigating to web view for recaptcha verification and coming back to the screen then the bloc listener not working. please help.

rashedswen commented 1 year ago

provide code example so we can help you

felangel commented 1 year ago

Hi @Div-47, as @rashedswen mentioned, you will need to provide a link to a minimal reproduction sample in order for us to help, thanks 👍

Div-47 commented 1 year ago
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:meta/meta.dart';
part 'otp_event.dart';
part 'otp_state.dart';

class OtpBloc extends Bloc<OtpEvent, OtpState> {
  OtpBloc() : super(OtpInitial()) {
    on<InitOTP>((event, emit) async {
      emit(InitOTPLoading());
      await _InitOtpVerfication(event, emit);
    });
    on<SubmitOTP>((event, emit) async {
      emit(SubmitOTPLoading());
      await _SubmitOtp(event, emit);
    });
    on<OnOTPSent>((event, emit) async {
      emit(InitOTPSuccess(event.verificationId));
    });
    on<OnVerificationFailed>((event, emit) {
      emit(OTPError(event.message));
    });
  }
}

_SubmitOtp(SubmitOTP event, Emitter<OtpState> emit) async {
  PhoneAuthCredential credential = PhoneAuthProvider.credential(
      verificationId: event.verificationId, smsCode: event.otp);
  await FirebaseAuth.instance.currentUser!.updatePhoneNumber(credential);
  emit(SubmitOTPSuccess("Success"));
}

Future _InitOtpVerfication(InitOTP event, Emitter<OtpState> emit) async {
  int? _resendToken;
  await FirebaseAuth.instance.verifyPhoneNumber(
    phoneNumber: event.phoneNumber,
    verificationCompleted: (PhoneAuthCredential credential) async {},
    verificationFailed: await (FirebaseAuthException e) {
      OtpBloc().add(OnVerificationFailed(e.message ?? "Error occured!"));
    },
    codeSent: await (String verificationId, int? resendToken) async {
      OtpBloc().add(OnOTPSent(verificationId));
      _resendToken = resendToken;
    },
    forceResendingToken: _resendToken,
    codeAutoRetrievalTimeout: (String verificationId) {},
  );
}

---- this is my bloc, you can see im adding event on code sent callback and then on this event i'm emiting an state. so that state is not updating and bloc listener is not listing to this state. it got stuck with the InitOTPLoading. During this process the firebase automatic nagivating to recaptcha webview verification and then come back to screen. so i think due to this the bloc stops listening the emitted states.

rashedswen commented 1 year ago

@Div-47 can you format your code? like this

class OTPBody extends StatefulWidget {
  const OTPBody({super.key});

  @override
  State< OTPBody > createState() =>
      _OTPBodyState();
}
Div-47 commented 1 year ago
import 'package:firebase_auth/firebase_auth.dart';
import "package:flutter/material.dart";
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:service_provider/bloc/Otp/otp_bloc.dart';

import 'package:service_provider/utility.dart/baseUtility.dart';
import 'package:service_provider/utility.dart/colors.dart';
import 'package:service_provider/utility.dart/navigation_utility.dart';
import 'package:service_provider/utility.dart/text_style.dart';
import 'package:service_provider/widgets/AlertDialogs.dart';
import 'package:service_provider/widgets/buttons.dart';

import 'add_profile.dart';

class OtpCode extends StatefulWidget {
  final String phoneNumber;
  final bool isCustomer;
  OtpCode(this.phoneNumber, this.isCustomer, {Key? key}) : super(key: key);

  @override
  _OtpCodeState createState() => _OtpCodeState();
}

class _OtpCodeState extends State<OtpCode> {
  final TextEditingController otpController1 = TextEditingController();
  final TextEditingController otpController2 = TextEditingController();
  final TextEditingController otpController3 = TextEditingController();
  final TextEditingController otpController4 = TextEditingController();
  final TextEditingController otpController5 = TextEditingController();
  final TextEditingController otpController6 = TextEditingController();

  @override
  void initState() {
    context.read<OtpBloc>()..add(InitOTP(widget.phoneNumber));
    print("init");

    // TODO: implement initState
    super.initState();
  }

  @override
  void didChangeDependencies() {
    print("change");
    super.didChangeDependencies();
  }

  @override
  void didUpdateWidget(covariant OtpCode oldWidget) {
    print("update");
    // TODO: implement didUpdateWidget
    super.didUpdateWidget(oldWidget);
  }

  @override
  Widget build(BuildContext context) {
    return BlocConsumer<OtpBloc, OtpState>(
      listener: (context, state) {
        if (state is SubmitOTPSuccess) {
          ScaffoldMessenger.of(context)
              .showSnackBar(SnackBar(content: Text(state.message)));
          navigateForward(context, AddProfile(widget.isCustomer));
          showDilaogBox(context, addProfilePicture(context));
        }
        if (state is OTPError) {
          ScaffoldMessenger.of(context)
              .showSnackBar(SnackBar(content: Text(state.message)));
        }
      },
      builder: (context, state) {
        return Scaffold(
          appBar: AppBar(
            backgroundColor: app_color,
            title: Text("OTP CODE", style: headingStyle20MBWhite()),
          ),
          body: GestureDetector(
            onTap: () {
              FocusScopeNode currentFocus = FocusScope.of(context);
              if (!currentFocus.hasPrimaryFocus) {
                currentFocus.unfocus();
              }
            },
            child: Container(
              height: double.infinity,
              width: double.infinity,
              color: app_background_color,
              padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 20),
              child: SafeArea(
                  child: SingleChildScrollView(
                child: Column(
                  children: [
                    const SizedBox(
                      height: 40,
                    ),
                    Container(
                      height: MediaQuery.of(context).size.height * .75,
                      padding: const EdgeInsets.all(10),
                      decoration: BoxDecoration(
                          borderRadius: BorderRadius.circular(5), color: white),
                      child: Column(children: [
                        const SizedBox(
                          height: 40,
                        ),
                        Text(
                          "Enter the otp code we texted to you phone number.",
                          style: headingStyle14MBDarkGrey(),
                          textAlign: TextAlign.center,
                        ),
                        const SizedBox(
                          height: 70,
                        ),
                        Row(
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: [
                            _textFieldOTP(otpController1,
                                first: true, last: false),
                            const SizedBox(width: 10),
                            _textFieldOTP(otpController2,
                                first: false, last: false),
                            const SizedBox(width: 10),
                            _textFieldOTP(otpController3,
                                first: false, last: false),
                            const SizedBox(width: 10),
                            _textFieldOTP(otpController4,
                                first: false, last: false),
                            const SizedBox(width: 10),
                            _textFieldOTP(otpController5,
                                first: false, last: false),
                            const SizedBox(width: 10),
                            _textFieldOTP(otpController6,
                                first: false, last: true)
                          ],
                        ),
                        const SizedBox(
                          height: 70,
                        ),
                        Padding(
                            padding: const EdgeInsets.all(8.0),
                            child: Row(
                              mainAxisAlignment: MainAxisAlignment.spaceBetween,
                              children: [
                                Text(
                                  "No Code Received?",
                                  style: headingStyle14MBDarkGrey(),
                                ),
                                Text(
                                  "Resend Now!",
                                  style: headingStyle16MBAppColor(),
                                )
                              ],
                            )),
                        const SizedBox(
                          height: 70,
                        ),
                        Padding(
                          padding: const EdgeInsets.symmetric(horizontal: 10.0),
                          child: GestureDetector(
                              onTap: () {
                                String otp = otpController1.text +
                                    otpController2.text +
                                    otpController3.text +
                                    otpController4.text +
                                    otpController5.text +
                                    otpController6.text;
                                if (state is InitOTPSuccess) {
                                  context.read<OtpBloc>()
                                    ..add(SubmitOTP(otp, state.verificationId));
                                }
                              },
                              child: button("Continue",
                                  isLoading: state is SubmitOTPLoading
                                      ? true
                                      : false)),
                        )
                      ]),
                    )
                  ],
                ),
              )),
            ),
          ),
        );
      },
    );
  }

  Widget _textFieldOTP(TextEditingController controller, {bool? first, last}) {
    return Container(
      // padding: EdgeInsets.only(left:5,right:5),
      height: 50,
      child: AspectRatio(
        aspectRatio: 0.8,
        child: TextField(
          controller: controller,
          autofocus: true,
          onChanged: (value) {
            if (value.length == 1 && last == false) {
              FocusScope.of(context).nextFocus();
            }
            if (value.isEmpty && first == false) {
              FocusScope.of(context).previousFocus();
            }
          },
          showCursor: false,
          readOnly: false,
          textAlign: TextAlign.center,
          style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
          keyboardType: TextInputType.number,
          maxLength: 1,
          decoration: InputDecoration(
            counter: Offstage(),
            enabledBorder: OutlineInputBorder(
                borderSide: BorderSide(width: 2, color: Colors.black12),
                borderRadius: BorderRadius.circular(12)),
            focusedBorder: OutlineInputBorder(
                borderSide: BorderSide(width: 2, color: Colors.purple),
                borderRadius: BorderRadius.circular(12)),
          ),
        ),
      ),
    );
  }

  @override
  void dispose() {
    print("dispose");
    // TODO: implement dispose
    super.dispose();
  }
}
rashedswen commented 1 year ago

I think you need to specify events added in this snippest

class OtpBloc extends Bloc<OtpEvent, OtpState> {
  OtpBloc() : super(OtpInitial()) {
    on((event, emit) async {
      emit(InitOTPLoading());
      await _InitOtpVerfication(event, emit);
    });
    on((event, emit) async {
      emit(SubmitOTPLoading());
      await _SubmitOtp(event, emit);
    });
    on((event, emit) async {
      emit(InitOTPSuccess(event.verificationId));
    });
    on((event, emit) {
      emit(OTPError(event.message));
    });
  }
}

like

on<OTPSubmit>((event, emit) async {
      emit(SubmitOTPLoading());
      await _SubmitOtp(event, emit);
 });

because bloc needs to know what event is submitted in order to execute specific code

this is what it needs to know

on<EVENT>((event, emit) async {
      // some code to execute
 });
Div-47 commented 1 year ago
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:meta/meta.dart';
part 'otp_event.dart';
part 'otp_state.dart';

class OtpBloc extends Bloc<OtpEvent, OtpState> {
  OtpBloc() : super(OtpInitial()) {
    on<InitOTP>((event, emit) async {
      emit(InitOTPLoading());
      await _InitOtpVerfication(event, emit);
    });
    on<SubmitOTP>((event, emit) async {
      emit(SubmitOTPLoading());
      await _SubmitOtp(event, emit);
    });
    on<OnOTPSent>((event, emit) async {
      emit(InitOTPSuccess(event.verificationId));
    });
    on<OnVerificationFailed>((event, emit) {
      emit(OTPError(event.message));
    });
  }
}

_SubmitOtp(SubmitOTP event, Emitter<OtpState> emit) async {
  PhoneAuthCredential credential = PhoneAuthProvider.credential(
      verificationId: event.verificationId, smsCode: event.otp);
  await FirebaseAuth.instance.currentUser!.updatePhoneNumber(credential);
  emit(SubmitOTPSuccess("Success"));
}

Future _InitOtpVerfication(InitOTP event, Emitter<OtpState> emit) async {
  int? _resendToken;
  await FirebaseAuth.instance.verifyPhoneNumber(
    phoneNumber: event.phoneNumber,
    verificationCompleted: (PhoneAuthCredential credential) async {},
    verificationFailed: await (FirebaseAuthException e) {
      OtpBloc().add(OnVerificationFailed(e.message ?? "Error occured!"));
    },
    codeSent: await (String verificationId, int? resendToken) async {
      OtpBloc().add(OnOTPSent(verificationId));
      _resendToken = resendToken;
    },
    forceResendingToken: _resendToken,
    codeAutoRetrievalTimeout: (String verificationId) {},
  );
}

yes, its already like this that you told me, i have already specified the events in the bloc. please check

Div-47 commented 1 year ago

I have got the issue. Thanks for helping me out.