felangel / bloc

A predictable state management library that helps implement the BLoC design pattern
https://bloclibrary.dev
MIT License
11.76k stars 3.39k forks source link

fix: BlocBuilder doesn't receive state when emitted from async loop #4197

Closed Seferi closed 2 months ago

Seferi commented 3 months ago

Description Hi there, I'm facing this issue where the state (RefreshedContactCount) is emitted but for some reason not being received by the BlocBuilder. All the other states are received and updating the UI just fine but RefreshedContactCount. Here is the method in the cubit which emits the state:


Future<void> refreshAllContacts() async {
    bool hasInternet = await InternetConnection().hasInternetAccess;
    if (!hasInternet) {
      emit(RefreshAllContactsFailure());
    } else {
      int refreshedContactsCount = 0;
      List<ProfileModel> contacts = getIt<UserCubit>().keyoxideUser.contacts;

      await Future.forEach(contacts, (contact) async {

        emit(RefreshingAllContacts());
        bool isContactRefreshed = await refreshContact(contact: contact, isRefreshingAllContacts: true);
        if (isContactRefreshed) {
          refreshedContactsCount++;

          emit(RefreshedContactCount(successfulRefreshCount: refreshedContactsCount));
        }
      });
      emit(RefreshAllContactsSuccess());
    }
  }

And this is the BlocBuilder which is suppose to receive the state:

BlocBuilder<ContactsCubit, ContactsState>(
  bloc: getIt<ContactsCubit>(),
  builder: (context, state) {
    if (state is RefreshingAllContacts) {

    }
    if (state is RefreshedContactCount) {
      refreshedContactCount = state.successfulRefreshCount;
    }
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 20.0),
      child: Center(
        child: Container(
            width: double.infinity,
            height: 200,
            decoration: BoxDecoration(
                color: Theme.of(context).brightness == Brightness.dark
                    ? Colors.black.withOpacity(0.9)
                    : Colors.white.withOpacity(0.9),
                borderRadius: const BorderRadius.all(Radius.circular(20))),
            child: Center(
                    child: Column(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        const CircularProgressIndicator(),
                        const SizedBox(height: 10),
                        Text("contacts_refresh_all_count".localizeWithPlaceholders(
                              [refreshedContactCount.toString(), totalContactCount.toString()])!,
                          textAlign: TextAlign.center,
                        ),
                      ],
                    )
            )),
      ),
    );
  },
);

Expected Behavior State gets received by the BlocBuilder or Consumer and updates the UI.

Additional Context I have been using Bloc for years now and never faced this kind of issue, I'm guessing the loop might be causing the issue, causing some kind of race condition. I'd very much appreciate if someone could help or take a look. Thanks.

FarisArmoush commented 3 months ago

Where is the variable refreshedContactCount coming from?

Seferi commented 3 months ago

First method line 6, if isContactRefreshed true, it increases the refreshedContactCount and UI should update the text in the dialog like ' 1 of 5 contacts are refreshed' to show the progress.

Project is open source, here is the source code: https://codeberg.org/Berker/keyoxide-flutter/src/branch/offline_support

and here is the sections of the code:

https://codeberg.org/Berker/keyoxide-flutter/src/branch/offline_support/lib/features/contacts/cubit/contacts_cubit.dart

https://codeberg.org/Berker/keyoxide-flutter/src/branch/offline_support/lib/features/contacts/widget/refresh_all_contacts_overlay.dart

felangel commented 3 months ago

Hi @Seferi 👋 Thanks for opening an issue!

Are you able to create a simple, minimal reproduction sample? It would be much easier to help if you could isolate the issue and provide a super simple, example with minimal dependencies, thanks!

felangel commented 2 months ago

Closing for now but if this is still an issue feel free to provide a minimal reproduction sample and I'm happy to take a closer look 👍