Vinayak
Vinayak

Reputation: 33

Bloc listener not invoked without a delay

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.

Upvotes: 0

Views: 724

Answers (2)

Huy Nguyen
Huy Nguyen

Reputation: 1109

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.

Upvotes: 1

Pieter van Loon
Pieter van Loon

Reputation: 5648

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.

Upvotes: 1

Related Questions