BlocListener Only returning intial loading state

Issue

I am building an app with flutter bloc. The issue i have is my bloc listener is only firing the initial state and not subsequent state change. All other questions have not been helpful as my state extends equatable to compare state. Here is my code below;

my login bloc

import 'package:bloc/bloc.dart';
import 'package:mobile_app/classes/custom_exception.dart';
import 'package:mobile_app/repositories/auth_repository.dart';
import 'package:mobile_app/states/login_status.dart';

class LoginBloc extends Cubit<LoginState> {
   LoginBloc(this.auth) : super(LoginState.initial());

   final AuthRepository auth;

   void login(String email, String password) async {
      emit(state.copyWith(loginStatus: Status.LOADING, isAuthenticated: false));
      final response = await auth.doLogin(email, password);
      if (response is AppException) {
        emit(state.copyWith(
           loginStatus: Status.ERROR,
           error: response.toString(),
           isAuthenticated: false));
     } else {
       emit(
          state.copyWith(loginStatus: Status.COMPLETED, isAuthenticated: true));
     }
  }
}

My state file;


enum Status { INITIAL, LOADING, COMPLETED, ERROR }

class LoginState extends Equatable {
final Status loginStatus;
final String? error;
final bool isAuthenticated;

LoginState(
   {required this.loginStatus, this.error, required this.isAuthenticated});

factory LoginState.initial() {
 return LoginState(loginStatus: Status.INITIAL, isAuthenticated: false);
}

LoginState copyWith(
   {required Status loginStatus,
   String? error,
   required bool isAuthenticated}) {
 return LoginState(
     loginStatus: loginStatus,
     error: error,
     isAuthenticated: isAuthenticated);
}

@override
List<Object?> get props => [loginStatus, error, isAuthenticated];
}

Then my listener

return BlocListener<LoginBloc, LoginState>(
      listener: (context, state) {
        if (state.loginStatus == Status.COMPLETED) {
          Navigator.of(context).pushReplacementNamed('/dashboard');
        }
        if (state.loginStatus == Status.ERROR) {
          final snackBar = SnackBar(
            backgroundColor: Colors.black,
            content: Text(state.error!),
          );
          ScaffoldMessenger.of(context).showSnackBar(snackBar);
        }
        print(state);
      },

I understand the listener is only called once for every state change but it’s as if the listener is not registering any state change. Help will be appreciated!

Solution

Okay so i think i know where the error is coming from. i have a blocbuilder that is showing different pages based on the current state and on of those pages include the login page that has the bloc listener. So i removed the bloc builder and just returned the login page with the bloc listener the snackbar is called as it should be. I used a blocconsumer to achieve what i want to achieve.

class LoginScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocConsumer<LoginBloc, LoginState>(
      listener: (context, state) {
        if (state is LoginError) {
          final snackBar = SnackBar(
            backgroundColor: Colors.black,
            content: Text(state.error),
          );
          ScaffoldMessenger.of(context).showSnackBar(snackBar);
        }
      },
      builder: (context, state) {
        if (state is LoginLoading) {
          return ProgressIndication();
        } else if (state is LoginSuccess) {
          return DashboardScreen();
        }
        return Login();
      },
    );
  }
}

Answered By – Patrick Obafemi

Answer Checked By – Timothy Miller (FlutterFixes Admin)

Leave a Reply

Your email address will not be published.