I'm using flutter Bloc to navigate the user toward either the login page or home screen depending on whether 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 in 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
AuthenticationState
code? – Athenaathenaeum