Search code examples
flutterdartbloc

I trying to use Flutter Bloc Pattern But I am facing some issue which is taping login button twice is setting the email and password value null


This is the state class Code

class SignInState {
  String email;
  String password;
  bool passwordVisible;

  SignInState(
      {this.email = "", this.password = "", this.passwordVisible = false});

  List<Object> get props => [this.email, this.password, this.passwordVisible];

  SignInState copyWith(
      {String? email, String? password, bool? passwordVisible}) {
    return SignInState(
        email: email ?? this.email,
        password: password ?? this.password,
        passwordVisible: passwordVisible ?? this.passwordVisible);
  }
}

// class SignInInitial extends SignInState {
//   SignInInitial({
//     String email = "",
//     String password = "",
//     bool passwordVisible = false,
//   }) : super(
//             email: email, password: password, passwordVisible: passwordVisible);
// }

class SignInLoading extends SignInState {}

class SignInSuccess extends SignInState {}

class SignInError extends SignInState {
  final String error;
  SignInError({required this.error});
}

This is the event class code

    @immutable
    sealed class SignInEvent {}
    
    class SignInEmailEvent extends SignInEvent {
      final String email;
    
      SignInEmailEvent({required this.email});
    }
    
    class SignInPasswordEvent extends SignInEvent {
      final String password;
    
      SignInPasswordEvent({required this.password});
    }
    
    class SignInPasswordVisibleEvent extends SignInEvent {
      final bool passwordVisible;
    
      SignInPasswordVisibleEvent({required this.passwordVisible});
    }
    
    class SignInLoginButtonEvent extends SignInEvent {}
    
    class SignInRegisterButtonEvent extends SignInEvent {
      final String email;
      final String password;
    
      SignInRegisterButtonEvent({required this.email, required this.password});
    }

**This is the BloC Code**


class SignInBloc extends Bloc<SignInEvent, SignInState> {
  SignInBloc() : super(SignInState()) {
    on<SignInEmailEvent>((event, emit) {
      // print("Email is ${event.email}");
      emit(state.copyWith(
        email: event.email,
      ));
    });
    on<SignInPasswordEvent>((event, emit) {
      // print("Email is ${event.password}");
      emit(state.copyWith(password: event.password));
    });
    on<SignInPasswordVisibleEvent>(
      (event, emit) =>
          emit(state.copyWith(passwordVisible: event.passwordVisible)),
    );
    on<SignInLoginButtonEvent>(
      (event, emit) async {
        String email = state.email;
        String password = state.password;

        print("Email is $email" + "Password is $password");

        if (email.isEmpty) {
          // print("Please enter email and password");
          emit(SignInError(error: "Please Enter Email"));
        } else if (password.isEmpty) {
          // print("Please enter email and password");
          emit(SignInError(error: "Please Enter password"));
        }
        try {
          final credential = await FirebaseAuth.instance
              .signInWithEmailAndPassword(email: email, password: password);
          // print("I am Here");
          if (credential.user == null) {
            // print("User Not Found");
            emit(SignInError(error: "User Not Found"));
          }
          if (credential.user!.emailVerified == false) {
            // print("Email Not Verified");
            emit(SignInError(error: "Email Not Verified"));
          }
          var user = credential.user;
          if (user != null) {
            emit(SignInSuccess());
            //emit(SignInSuccess());
          }
        } on FirebaseAuthException catch (e) {
          if (e.code == "user-not-found") {
            //print("User Not Found");
            emit(SignInError(error: "User Not Found"));
          } else if (e.code == 'wrong-password') {
            //print("Wrong Password");
            emit(SignInError(error: "Wrong Password"));
          } else if (e.code == 'invalid-email') {
            //print("Invalid Email");
            emit(SignInError(error: "Invalid Email"));
          }
        }
      },
    );
  }
}

I couldn't find where I am doing it wrong. Typing email and pressing the signup button print- Email is miraj Password is Typing pass and pressing the signup button print-Email is Password is 123 Why is the email/password setting null? Also, setting the email and passing it together print-Email is miraj Password is 123 tapping again **print-Email is Password is ** Both are set to null. How can I resolve this problem?


Solution

  • As soon as you call emit(SignInSuccess()); you are changing your current state to a state without any data in email and password.

    Don't use the state property of the BaseBloc class, instead supply the variables through the event.

    The form triggers the SignInLoginEvent which will be handled by the bloc. A bloc basically translates events to states. The UI uses a BlocBuilder to show different things depending on the state which was emited from the bloc. Please note that the BlocBuilder needs to be a (not necessarily direct) child of a BlocProvider of the same type.

    Events

    @immutable
    sealed class SignInEvent {}
    
    class SignInLoginEvent extends SignInEvent {
      final String? email;
      final String? password;
    
      SignInLoginEvent({this.email, this.password});
    }
    

    States

    class SignInState {}
    
    class InitialState extends SignInState {}
    
    class SignInLoading extends SignInState {}
    
    class SignInSuccess extends SignInState {}
    
    class SignInError extends SignInState {
      final String error;
    
      SignInError({required this.error});
    }
    

    Bloc

    class SignInBloc extends Bloc<SignInEvent, SignInState> {
      SignInBloc() : super(InitialState()) {
        on<SignInLoginEvent>(
          (event, emit) async {
            String? email = event.email;
            String? password = event.password;
    
            if (kDebugMode) {
              print("Email is $email and password is $password");
            }
    
            if (email != null && email.isEmpty) {
              emit(SignInError(error: "Please Enter Email"));
            } else if (password != null && password.isEmpty) {
              emit(SignInError(error: "Please Enter password"));
            }
    
            emit(SignInLoading());
    
            try {
              final credential = await FirebaseAuth.instance
                  .signInWithEmailAndPassword(email: email, password: password);
              if (credential.user == null) {
                emit(SignInError(error: "User Not Found"));
              }
              if (credential.user!.emailVerified == false) {
                emit(SignInError(error: "Email Not Verified"));
              }
              var user = credential.user;
              if (user != null) {
                emit(SignInSuccess());
              }
            } on FirebaseAuthException catch (e) {
              if (e.code == "user-not-found") {
                emit(SignInError(error: "User Not Found"));
              } else if (e.code == 'wrong-password') {
                emit(SignInError(error: "Wrong Password"));
              } else if (e.code == 'invalid-email') {
                emit(SignInError(error: "Invalid Email"));
              }
            }
          },
        );
      }
    }
    

    Login Form

    class Login extends StatefulWidget {
      const Login({Key? key}) : super(key: key);
    
      @override
      _LoginState createState() => _LoginState();
    }
    
    class _LoginState extends State<Login> {
      @override
      Widget build(BuildContext context) {
        return BlocProvider(
          create: (context) => SignInBloc(),
          child: const LoginPage(),
        );
      }
    }
    
    class LoginPage extends StatefulWidget {
      const LoginPage({Key? key}) : super(key: key);
    
      @override
      _LoginPageState createState() => _LoginPageState();
    }
    
    class _LoginPageState extends State<LoginPage> {
      @override
      Widget build(BuildContext context) {
        String? email, password;
        return BlocBuilder<SignInBloc, SignInState>(
          builder: (context, state) => Form(
            child: Column(
              children: [
                TextFormField(
                  decoration: const InputDecoration(label: Text('Username')),
                  onChanged: (value) => email = value,
                ),
                TextFormField(
                  decoration: const InputDecoration(label: Text('Password')),
                  obscureText: true,
                  onChanged: (value) => password = value,
                ),
                ElevatedButton(onPressed: () => {
                  context.read<SignInBloc>().add(SignInLoginEvent(email: email, password: password));
                }, child: const Text('Login')),
                switch (state) {
                  SignInLoading() => const Text('Trying to sign in...'),
                  SignInSuccess() => const Text('Successful signed in'),
                  SignInError() => Text(state.error),
                  _ => const Text(''),
                }
              ],
            ),
          ),
        );
      }
    }