Search code examples
flutterdartlistenerbloc

Bloc listener not triggered


I have a login screen. When logo is clicked, I want display a loading on top of it. The loading widget I put in ContainerWithLoadingOverlay.

main.dart

class App extends StatelessWidget {
  const App({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MultiBlocProvider(
      providers: [
        BlocProvider(create: (_) => sl<OverlayLoadingCubit>()),
      ],
      child: MaterialApp(
        debugShowCheckedModeBanner: false,
        localizationsDelegates: [
          EasyLocalization.of(context)!.delegate,
        ],
        supportedLocales: EasyLocalization.of(context)!.supportedLocales,
        locale: EasyLocalization.of(context)!.locale,
        title: kAppTitle,
        theme: AppTheme.light,
        onGenerateRoute: AppRoute.onGenerateRoutes,
      ),
    );
  }
}

log_in_screen.dart

 return Scaffold(
        body: ContainerWithLoadingOverlay(    <--- this for loading 
      child: SingleChildScrollView(
          child: Padding(
              padding: const EdgeInsets.all(32.0),
              child: Center(
                child: Column(
                  children: [
                    const AppLogo(
                      width: 150,
                      height: 150,
                    ),
                    const SizedBox(height: 32),
                    InkWell(
                        onTap: () {
                          context.read<LogInCubit>().signIn(
                              SignInParams(
                                  email: _emailController.text,
                                  password: _passwordController.text));
                        },
                        child: Text(
                          "Welcome back.",
                          style: Theme.of(context).textTheme.headline4,
                        )),
                    const SizedBox(height: 32),
                    // _loginForm(context, isShown),
                  ],
                ),
              ))),
    ));
  }

log_in_cubit.dart

class LogInCubit extends Cubit<LogInState> {
  final OverlayLoadingCubit _loadingCubit;
  final SignInUseCase _useCase;

  LogInCubit(this._useCase, this._loadingCubit) : super(LogInLoading());

  Future<void> signIn(SignInParams params) async {
    _loadingCubit.startLoading();  
           ...
  }
}

overlay_loading_state.dart

part of 'overlay_loading_cubit.dart';

abstract class OverlayLoadingState {
  const OverlayLoadingState();
}

class OverlayLoadingInitial extends OverlayLoadingState {}

class IsLoadingState extends OverlayLoadingState {}

overlay_loading_cubit.dart

class OverlayLoadingCubit extends Cubit<OverlayLoadingState> {
  OverlayLoadingCubit() : super(OverlayLoadingInitial());

   startLoading() {
    debugPrint("start loading");  <-- this line is able to print
    emit(IsLoadingState());  
    return;
  }
}

container_with_loading_overlay.dart

class ContainerWithLoadingOverlay extends HookWidget {
  final Widget child;

  const ContainerWithLoadingOverlay({Key? key, required this.child})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return BlocListener<OverlayLoadingCubit, OverlayLoadingState>(
      listener: (context, state) {
        debugPrint("call from listener");  <--- this line not calling
        if (state is OverlayLoadingInitial) {
          context.loaderOverlay.hide();
        }

        if (state is IsLoadingState) {
          context.loaderOverlay.show();
        }
      },
      child: LoaderOverlay(
        useDefaultLoading: false,
        overlayWidget: const Center(child: CircularProgressIndicator()),
        child: child,
      ),
    );
  }
}

I expect the bloc listener in class ContainerWithLoadingOverlay will get triggered and loading dialog will show, but it not triggred..what's the issue?


Solution

  • The BlocListener is triggered only when it detects a state change. If your BlocListener is not being triggered, try emitting an OverlayLoadingInitial() state first, wait for a second, and then emit the isLoadingStates state.

    Here's an example code:

    void startLoading() {
      // debugPrint("start loading");  <-- this line is able to print
      emit(OverlayLoadingInitial());
      await Future.delayed(const Duration(seconds: 1)); // Emulates your signIn request
      emit(IsLoadingState());
      return;
    }