Flutter Application Bloc listener not receiving state updates

Viewed 3415

I'm using flutter Bloc to navigate the user towards either the login page or home screen depending on wether they are authenticated or not. However, after the initial state change, the listener doesn't trigger when I change my authentication state.

Listener:

Widget build(BuildContext context) {
  return BlocListener<AuthenticationBloc, AuthenticationState>(
    listener: (context, state) {
      // Listener never gets called on statechange after the initial app startup

      // navigation based on authentication 
      }
    },
    child: SplashPage(),
  );
}

The provider gets initialized in the parent widget:

AuthenticationRepository authRepo = AuthenticationRepository();

Widget build(BuildContext context) {
  return MultiBlocProvider(
    providers: [
      BlocProvider<AuthenticationBloc>(
        create: (BuildContext context) =>
            AuthenticationBloc(authenticationRepository: authRepo),
      ),
      /*
         Other Providers
      */
    ],
    child: MaterialApp(
      title: 'myApp',
      home: StartUpPage(),
    ),
  );

When the user logs in mapEventState gets called in the AuthenticationBloc:

class AuthenticationBloc
    extends Bloc<AuthenticationEvent, AuthenticationState> {
  AuthenticationBloc({
    @required AuthenticationRepository authenticationRepository,
  })  : assert(authenticationRepository != null),
        _authenticationRepository = authenticationRepository,
        super(const AuthenticationState.unknown()) {
    _userSubscription = _authenticationRepository.userStream.listen(
      (user) => add(AuthenticationUserChanged(user)),
    );
  }

  final AuthenticationRepository _authenticationRepository;
  StreamSubscription<User> _userSubscription;

  @override
  Stream<AuthenticationState> mapEventToState(
    AuthenticationEvent event,
  ) async* {
    if (event is AuthenticationUserChanged) {
      yield _mapAuthenticationUserChangedToState(event);
    } else if (event is AuthenticationLogoutRequested) {
      unawaited(_authenticationRepository.logOut());
    }
  }

  @override
  Future<void> close() {
    _userSubscription?.cancel();
    return super.close();
  }

  AuthenticationState _mapAuthenticationUserChangedToState(
    AuthenticationUserChanged event,
  ) =>
      event.user != User.empty
          ? AuthenticationState.authenticated(event.user)
          : const AuthenticationState.unauthenticated();
}

I'd expect the listener to trigger when the user logs in and the AuthenticationState changes. If anyone knows what I'm doing wrong or if I'm missing something I'd love to hear it.

EDITED 08-02-2021:

I've checked again if the state changes after login using a simple button. With this I can confirm that the state does change and holds the correct user data and authentication status. Another thing I confirmed is that a BlocBuilder that is using a BlocBuilder<AuthenticationBloc, AuthenticationState> IS updating correctly when a user logs in.

EDITED 10-02-2021:

Entire Authentication state:

enum AuthenticationStatus { authenticated, unauthenticated, unknown }

class AuthenticationState extends Equatable {
  const AuthenticationState._({
    this.status = AuthenticationStatus.unknown,
    this.user = User.empty,
  });

  const AuthenticationState.unknown() : this._();

  const AuthenticationState.authenticated(User user)
      : this._(status: AuthenticationStatus.authenticated, user: user);

  const AuthenticationState.unauthenticated()
      : this._(status: AuthenticationStatus.unauthenticated, user: User.empty);

  final AuthenticationStatus status;
  final User user;

  @override
  List<Object> get props => [status, user];
}

EDITED 12-02-2021:

removed non-relevant code

EDITED 15-02-2021:

Added entire Authentication BloC

3 Answers

Without seeing the BLoC it's hard to tell but is it possible that you are creating the listener after the state been emitted. Bare in mind that Listener will not fire the last state, only the states been emitted after the creation of the listener.

Please share the BLoC code.

Can you list your entire widget tree?

I'm wondering if your first widget (the one containing the Listener) isn't actually being rendered (say, because you have an if/else somewhere in the tree that means it's not actually being shown at the time the event is fired).

You can check this with the Flutter inspector. Otherwise, is it running in the same BuildContext?

If you showed the whole widget tree, this would be easier to diagnose.

It looks like your AuthenticationRepository dispatches the AuthenticationEvent by listening to _authenticationRepository.userStream. I think the problem is that you create an instance of AuthenticationRepository earlier than AuthenticationBloc, this may cause the AuthenticationStatus change before the BLoC strats listening to it.

Though it would be better if you could provide AuthenticationRepository code.

Related