Search code examples
flutterdartbloc

Is it possible to not rebuild content in BlocBuilder?


I want to push snackbars if state is:

  • PostSuccess - show snackbar, go to previous screen by Navigator.pop();
  • Failed - show snackbar and not rebuild the listView.builder
              if (state is EduWorkPostSuccess) {
                showTopSnackBar(
                  Overlay.of(context),
                  CustomSnackBar.success(message: "Everything went alright"),
                );
                Navigator.pop(context);
              } else if (state is EduWorkFailed) {
                showTopSnackBar(
                  Overlay.of(context),
                  CustomSnackBar.error(
                      message: translation.translation[state.msg]),
                );
              }
            }, builder: (context, state) {
              if (state is EduWorkLoaded && state.isChanged) {
                return Padding(
                  padding: const EdgeInsets.all(15.0),
                  child: Center(
                    child: UserDataSectionDesription(
                      description:
                          "Remember to save your changes", //TODO added to translation file, w8ting...
                    ),
                  ),
                );
              } else {
                return SizedBox();
              }
            }
BlocBuilder<EduWorkBloc, EduWorkState>(
              builder: (context, state) {
                if (state is EduWorkLoading) {
                  return Center(
                      child: CircularProgressIndicator(
                    color: Theme.of(context).colorScheme.secondary,
                  ));
                } else if (state is EduWorkLoaded) {
                  return ListView.builder(
                    shrinkWrap: true,
                    itemCount: selectedMap.length,
                    itemBuilder: (context, index) {
                      return Padding(
                        padding: const EdgeInsets.all(8),
                        child: EducationWidget(
                          isSelected: state.eduWork.isSelectedEdu(
                              selectedMap.values.elementAt(index).toString()),
                          index: index,
                          code: selectedMap.values.elementAt(index).toString(),
                          name: selectedMap.keys.elementAt(index).toString(),
                          onPressed: () {
                            final codeName =
                                selectedMap.values.elementAt(index).toString();
                            //if that level of education is selected unselect it
                            if (state.eduWork.education.contains(codeName)) {
                              state.eduWork.education.remove(codeName);

                              BlocProvider.of<EduWorkBloc>(context)
                                  .add(ChangeEdu(state.eduWork));

                              //If unselected add it
                            } else {
                              state.eduWork.education.add(codeName);

                              BlocProvider.of<EduWorkBloc>(context)
                                  .add(ChangeEdu(state.eduWork));
                            }
                          },
                        ),
                      );
                    },
                  );
                }
                return Text('Error');
              },
            ),

For this moment, .success is working properly. When the state is Failed the snackbar shows properly, but it returns Text('error)'. How can I solve that?


Solution

  • You say "the state is Failed the snackbar shows properly, but it returns Text('error)'"

    In the BlockBuilder you have a "return Text('Error');"

    if you have an error, your state will be "EduWorkFailed", in your validations you have:

    "if (state is EduWorkLoading)" and "else if (state is EduWorkLoaded)".

    So when the state is EduWorkFailed, you can only return "Text('Error');" for the previous validations. You have to change the widget to show something different, put a new state, or contemplate "EduWorkFailed" in the BlockBuilder because when you emit a new state you have to handle that state or consider it in the BlocBuilder when you emit a state BlockBuilder has to refresh the child.

    https://pub.dev/documentation/flutter_bloc/latest/flutter_bloc/BlocBuilder-class.html

    An optional buildWhen can be implemented for more granular control over how often BlocBuilder rebuilds, that is the documentation

    (For test, if you don't use "Navigator.pop(context);" the widget shown should be "Text('Error');")

    in this part:

    if (state is EduWorkPostSuccess) {
                showTopSnackBar(
                  Overlay.of(context),
                  CustomSnackBar.success(message: "Everything went alright"),
                );
                Navigator.pop(context);
              }