0

I have defined the following cubit.

@injectable
class AuthCubit extends Cubit<AuthState> {
  final IAuthService _authService;

  AuthCubit(this._authService) : super(const AuthState.initial());

  void authCheck() {
    emit(_authService.signedInUser.fold(
      () => AuthState.unauthenticated(none()),
      (user) => AuthState.authenticated(user),
    ));
  }
}

But the BlocListener which listens to this bloc is not getting invoked even after emit is called. But everything works as expected when I add a zero delay before the emit call.

Future<void> authCheck() async {
  await Future.delayed(Duration.zero);
  emit(_authService.signedInUser.fold(
    () => AuthState.unauthenticated(none()),
    (user) => AuthState.authenticated(user),
  ));
}

I tried out this delay because for other events which made some backend call (with some delay) emit worked perfectly. But I'm pretty sure this is not how it should work. Am I missing something here?

EDIT: Adding the SplashPage widget code which uses BlocListener.

class SplashPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocListener<AuthCubit, AuthState>(
      listener: (context, state) {
        print(state);
      },
      child: Scaffold(
        body: Center(
          child: CircularProgressIndicator(),
        ),
      ),
    );
  }
}

Place where authCheck() is called,

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiBlocProvider(
      providers: [
        BlocProvider<AuthCubit>(
          create: (_) => getIt<AuthCubit>()..authCheck(),
        ),
      ],
      child: MaterialApp(
        ....
      ),
    );
  }
}

and the AuthState is a freezed union

@freezed
abstract class AuthState with _$AuthState {
  const factory AuthState.initial() = _Initial;
  const factory AuthState.authenticated(User user) = _Authenticated;
  const factory AuthState.unauthenticated(Option<AuthFailure> failure) = _Unauthenticated;
  const factory AuthState.authInProgress() = _AuthInProgress;
}

Also, when I implemented a bloc (instead of Cubit) with the same functionality, everything worked as expected.

Vinayak
  • 33
  • 2
  • 5
  • Can you add the code where authCheck is called and where the bloc is created – Pieter van Loon Oct 24 '20 at 19:48
  • @PietervanLoon Added the widget code which uses BlocListener. – Vinayak Oct 27 '20 at 11:03
  • That's not what I asked XD. But i now realise that it might be that the states get seen as the same state, so can you share the `AuthState` code – Pieter van Loon Oct 27 '20 at 13:33
  • oh, I misread your earlier comment. Edited with more info. Also, as written at the end, things were working fine when I converted this Cubit to a Bloc with exact same functionality. – Vinayak Oct 27 '20 at 20:24

2 Answers2

1

Without the delay the emit is called directly from the create method of the provider. This means that the listener is not (completely) built yet and thus there is no listener to be called when you emit the state.

So by adding the delay you allow the listener to subscribe to the stream first and thus it gets called when you emit the new state.

Pieter van Loon
  • 5,318
  • 1
  • 6
  • 15
1

For me, the delay does not work perfectly. So I found this solution, maybe help someone:

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance?.addPostFrameCallback((_) async {
      await myCubit.doSomethingFun();
    });
  }

And @Pieter is right, listener only be invoked when the widget is built.

Huy Nguyen
  • 1,032
  • 14
  • 38