Top of the widget tree I have blocprovider with "lazy: False" parameter (authCubit) and with this, I can immediately listen to the auth changes.
I also have a landing page, and it's exactly using BlocListener. The problem is, first the state is coming false from the firebase auth (user is null), then it will come as true (user is firebase user). Because of that, the BlocListener does not listen to the state and does nothing. I tried to catch it with "listenWhen" property, but I couldn't do it.
I also checked that when the app restarted, the user is not null, it comes as FirebaseUser.
In the landingPage which has the related blocListener, I tried to use WidgetsBinding.instance.addPostFrameCallback for controlling the state, but I couldn't handle it.
late StreamSubscription<AuthUserModel>? _authUserSubscription;
_authUserSubscription = _authService.authStateChanges.listen(_listenAuthStateChangesStream);
Future<void> _listenAuthStateChangesStream(AuthUserModel authUser) async {
emit(state.copyWith(isInProgress: true));
if (AuthUserModel.empty() == authUser) {
emit(
state.copyWith(
authUser: authUser,
isUserLoggedIn: false,
isInProgress: false,
),
);
} else {
await _chatService.connectTheCurrentUser();
emit(
state.copyWith(
authUser: authUser,
isUserLoggedIn: true,
isInProgress: false,
),
);
}
}
and my infrastructure layer,
@override
Stream<AuthUserModel> get authStateChanges {
return _firebaseAuth.authStateChanges().map(
(User? user) {
if (user == null) {
return AuthUserModel.empty();
} else {
return user.toDomain();
}
},
);
}
So, you can ask how do you use the blocProvider and bloc listener.
BlocProvider in the top of the widget tree:
BlocProvider(
lazy: false,
create: (context) => getIt<AuthCubit>(),
child: Listener(
onPointerUp: (_) {
if (Platform.isIOS) {
final FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus && currentFocus.focusedChild != null) {
FocusManager.instance.primaryFocus!.unfocus();
}
}
},
child: MaterialApp.router(...
Finally, BlocListener here:
@override
Widget build(BuildContext context) {
return BlocListener<AuthCubit, AuthState>(
listener: (context, state) {
if (state.isUserLoggedIn) {
context.go(context.namedLocation("channels_page"));
} else {
context.go(context.namedLocation("sign_in_page"));
}
},
child: const Scaffold(
body: Center(
child: CustomProgressIndicator(
progressIndicatorColor: Colors.black,
),
),
),
);
I just checked if the user checked from the auth service or not. With this, since we update our state before the BlocListener, we can check after the BlocListener, our state is what. Then, we can do some things using the information that is exactly the state before the BlocListener occurs and after the BlocListener occurs.
listenWhen: (p, c) =>
p.isUserCheckedFromAuthService != c.isUserCheckedFromAuthService &&
c.isUserCheckedFromAuthService,
So, the related function is like this:
Future<void> _listenAuthStateChangesStream(AuthUserModel authUser) async {
emit(
state.copyWith(
isInProgress: true,
authUser: authUser,
isUserCheckedFromAuthService: true,
),
);
if (state.isLoggedIn) {
await _chatService.connectTheCurrentUser();
emit(
state.copyWith(
authUser: authUser,
isInProgress: false,
),
);
}
}