Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

BlocBuilder not updating after cubit emit

UPDATE After finding the onChange override method it appears that the updated state is not being emitted #confused

UPDATE 2 Further debugging revealed that the StreamController appears to be closed when the updated state attempts to emit.

For some reason 1 of my BlocBuilders in my app refuses to redraw after a Cubit emit and for the life of me I cannot figure out why, there are no errors when running or debugging, the state data is being updated and passed into the emit.

Currently, it is a hook widget, but I have tried making it Stateless, Stateful, calling the cubit method in an effect, on context.bloc.

I have tried making it a consumer, nothing makes it into the listen, even tried messing with listenWhen and buildWhen and nothing is giving any indication as to why this is not building.

It renders the loading state and that is where it ends.

Widget:

class LandingView extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final hasError = useState<bool>(false);

    return Scaffold(
      key: const Key(LANDING_VIEW_KEY),
      body: BlocProvider<CoreCubit>(
        create: (_) => sl<CoreCubit>()..fetchDomainOptions(),
        child: BlocBuilder<CoreCubit, CoreState>(
          builder: (context, state) {
            switch (state.status) {
              case CoreStatus.loaded:
                return LandingViewLoaded();
              case CoreStatus.error:
                return LandingViewError();
              case CoreStatus.loading:
              default:
                return AppIcon();
            }
          },
        ),
      ),
    );
  }
}

Cubit method:

  Future<void> fetchDomainOptions() async {
    final inputEither = await getDomainOptions();

    return inputEither.fold(
      _handleFailure,
      (options) {
        emit(state.copyWith(
          domainOptions: options,
          status: CoreStatus.loaded,
        ));
      },
    );
  }

I have a few other widgets that work off freezed data classes and work of the same status key logic without any issues, I even went as far as trying to add a lastUpdated timestamp key onto it to make even more data change, but in the initial state domainOptions is null and status is CoreStatus.loading, which should already be enough to trigger a UI update.

TIA

like image 687
RemeJuan Avatar asked Oct 15 '20 11:10

RemeJuan


1 Answers

I haven't figured out yet why exactly this happens but I believe we're dealing with some kind of race condition here. Also, I don't know the proper solution to work around that but I have found that delaying calling emit() for a short amount of time prevents this issue from occurring.

void load() async {
  try {
    emit(LoadingState());
    final vats = await provider.all();
    await Future<void>.delayed(const Duration(milliseconds: 50));
    emit(LoadedState(vats));
  } catch (e) {
    print(e.toString());
    emit(ErrorState());
  }
}

Add await Future<void>.delayed(const Duration(milliseconds: [whatever is needed])); before emitting your new state.

I came to this conclusion after reading about this issue on cubit's GitHub: Emitted state not received by CubitBuilder

like image 112
onomio Avatar answered Sep 19 '22 09:09

onomio