1

I'm using a Cubit in my app and I'm struggling to understand one behavior.

I have a list of products and when I open the product detail screen I want to have a "blank" screen with a loading indicator until receiving the data to populate the layout, but the loading indicator is not being triggered in the listener (only in this first call, when making a refresh in the screen it shows the loader).

I'm using a BlocConsumer and i'm making the request in the builder when catching the ApplicationInitialState (first state), in cubit I'm emitting the ApplicationLoadingState(), but this state transition is not being caught in the listener, only when the SuccessState is emitted the listener triggers and tries to remove the loader.

I know the listener does not catch the first State emitted but I was expecting it to catch the first state transition.

UI

@override
void initState() {
  super.initState();
  WidgetsBinding.instance.addObserver(this);
}

@override
Widget build(BuildContext context) {
  _l10n = AppLocalizations.of(context);
  return _buildConsumer();
}

_buildConsumer() {
  return BlocConsumer<ProductCubit, ApplicationState>(
    bloc: _productCubit,
    builder: (context, state) {
      if (state is ApplicationInitialState) {
        _getProductDetail();
      }
      return Scaffold(
        appBar: _buildAppbar(state),
        body: _buildBodyState(state),
      );
    },
    listener: (previous, current) async {
      if (current is ApplicationLoadingState) {
        _loadingIndicator.show(context);
      } else {
        _loadingIndicator.close(context);
      }
    },
  );
}

Cubit

class ProductCubit extends Cubit<ApplicationState> with ErrorHandler {
  final ProductUseCase _useCase;

  ProductCubit({
    required ProductUseCase useCase,
  })  : _useCase = useCase,
        super(const ApplicationInitialState());

  void getProductDetail(String id) async {
    try {
      emit(const ApplicationLoadingState());

      final Product = await _useCase.getProductDetail(id);

      emit(CSDetailSuccessState(
        detail: ProductDetailMapper.getDetail(Product),
      ));
    } catch (exception) {
      emit(getErrorState(exception));
    }
  }
}

ApplicationLoadingState

abstract class ApplicationState extends Equatable {
  const ApplicationState();

  @override
  List<Object> get props => [];
}

class ApplicationLoadingState extends ApplicationState {
  const ApplicationLoadingState();
}
JACF
  • 117
  • 1
  • 5
  • please attach your ApplicationLoadingState,i think that's the problem – shadow Jul 13 '22 at 11:03
  • @shadow added the requested and clarified that the ApplicationLoadingState is not caught only in this transition from InitState --> ApplicationLoadingState, because when I refresh the screen the loader is displayed normally – JACF Jul 13 '22 at 11:19
  • We ended up using the addPostFrameCallback method to make the service call, ensuring this way that the listener will catch the first state transition. – JACF Aug 09 '22 at 11:19

0 Answers0