Dart: How to properly dispatch bloc event in another bloc

Issue

I need to access AuthenticationBloc in my LoginBloc so I can fire the AuthenticationLogin() event if the login is successful. What I did so far is not working.

What I’ve done:

class LoginBloc extends Bloc<LoginEvent, LoginState> {
  final AuthenticationBloc authenticationBloc;
  final AuthenticateCredentialsUsecase authenticateCredentialsUsecase;

//code

  Stream<LoginState> mapEventToState(
    LoginEvent event,
  ) async* {
        //code
        authenticationBloc.add(AuthenticationLogin());
        yield LoginLoadSuccess();
        //code
  }
}

What I’m trying to accomplish:

class _AppViewState extends State<AppView> {

  final _navigatorKey = GlobalKey<NavigatorState>();
  NavigatorState get _navigator => _navigatorKey.currentState;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      navigatorKey: _navigatorKey,
      builder: (context, child) {
        return BlocListener<AuthenticationBloc, AuthenticationState>(
          listener: (context, state) {
             if (state is AuthenticationAuthenticated) {
                _navigator.pushAndRemoveUntil<void>(
                  HomePage.route(),
                  (route) => false,
                );
             }
             else if (state is AuthenticationUnauthenticated){
                _navigator.pushAndRemoveUntil<void>(
                  LoginScreen.route(),
                  (route) => false,
                );
             }
          },
          child: child,
        );
      },
      onGenerateRoute: (_) => SplashPage.route(),
    );
  }
}

As you can see, the user is currently in the LoginScreen, once the login is successful, I need to yield the AuthenticationAuthenticated() state in my AuthenticationBloc() so my users will be directed to the HomePage()

How can I yield the AuthenticationAuthenticated() state of the AuthenticationBloc() inside my LoginBloc() – since my login logic happens inside the LoginBloc.

Solution

  1. I subscribed the AuthenticationBloc to the status stream of my AuthenticateCredentialsUsecase class.
  2. When the AuthenticateCredentialsUsecase is called in my LoginBloc and the credentials are authenticated…
  3. I then update the status stream – _controller.add(AuthenticationStatus.authenticated);
  4. Which inturn will trigger the AuthenticationLogin event
    inside the AuthenticationBloc

AuthenticationBloc

 AuthenticationBloc({
    @required CheckAuthenticationStatusUsecase checkAuthenticationStatus,
    @required LogoutAuthenticatedUserUsecase logoutAuthenticatedUser,
    @required AuthenticateCredentialsUsecase authenticateCredentials,
  })  : assert(checkAuthenticationStatus != null),
        assert(logoutAuthenticatedUser != null),
        assert(authenticateCredentials != null),
        checkAuthenticationStatusUsecase = checkAuthenticationStatus,
        logoutAuthenticatedUserUsecase = logoutAuthenticatedUser,
        authenticateCredentialsUsecase = authenticateCredentials,
        super(AuthenticationInitial()) {
    add(AuthenticationStatusRequested());
    _loginStatusSubscription =
        authenticateCredentialsUsecase.status.listen((event) {
      if (event == AuthenticationStatus.authenticated) {
        add(AuthenticationLogin());
      }
    });
  }

AuthenticateCredentialsUsecase

  final _controller = StreamController<AuthenticationStatus>();

  Stream<AuthenticationStatus> get status async* {
    yield AuthenticationStatus.unknown;
    yield* _controller.stream;
  }

  void dispose() => _controller.close();

  @override
  Future<Either<Failure, AuthenticatedUser>> call(AuthenticationParams params) async {

    final result = await repository.authenticateCredentials(params.userName, params.password);

    if(result is Right){
      _controller.add(AuthenticationStatus.authenticated);
    }

    return result;
  }

Answered By – Paula Ysabelle Medina

Answer Checked By – Jay B. (FlutterFixes Admin)

Leave a Reply

Your email address will not be published. Required fields are marked *