bottom sheet is showing repeatedly once event triggred using bloc

110

Problem definition: This problem happens because you use state.copyWith on your bloc. When you use copyWith, even if you don't update isAddNewNoteButtonClickedState variable, your state persists that value (stays true after you set it, never changes if you don't change it) if you do not alter it. Because on copyWith method, update logic works like;

YourState copyWith(bool isAddNewNoteButtonClickedState) {
   return YourState(isAddNewNoteButtonClickedState: isAddNewNoteButtonClickedState ?? this. isAddNewNoteButtonClickedState,)
}

And when you yield another state, this isAddNewNoteButtonClickedState stays true, and your listener shows another modal since your isAddNewNoteButtonClickedState did not change.

Solution: you can solve this problem by (adding isAddNewNoteButtonClickedState: false to other states):

    class NoteBloc extends Bloc<NoteEvent, NoteState> {
  NoteBloc() : super(NoteState.initial());

  @override
  Stream<NoteState> mapEventToState(
    NoteEvent event,
  ) async* {
    yield* event.map(
      addNewNoteButtonEvent: (AddNewNoteButtonEvent e) async* {
          yield state.copyWith(
            isAddNewNoteButtonClickedState: e.isClickedEvent,
          );
      },
      radioEvent: (RadioEvent e) async* {
        yield state.copyWith(radioGroupState: e.value, isAddNewNoteButtonClickedState: false);
      },
      textInputEvent: (TextInputEvent e) async* {
        yield state.copyWith(textInputState: Note(value: e.value), isAddNewNoteButtonClickedState: false);
      },
    );
  }
}

Additional note: Since you are not transforming events in bloc, or doing something trivial, you can use cubit to simplify your state management and business logic.

Share:
110
Winter
Author by

Winter

Updated on December 01, 2022

Comments

  • Winter
    Winter over 1 year

    I am a newbie, learning how to use bloc with freezed. i created a bottom sheet when the user click to the float action button, the bottom sheet appears. Bottom sheet contains text field and three radio groups when i click to select a radio the bottom sheet popup again like this GIF. https://drive.google.com/file/d/1iU06adGcwjEaw9z2LsmO5xS24CC6OQfT/view?usp=sharing

    the bloc is:

    class NoteBloc extends Bloc<NoteEvent, NoteState> {
      NoteBloc() : super(NoteState.initial());
    
      @override
      Stream<NoteState> mapEventToState(
        NoteEvent event,
      ) async* {
        yield* event.map(
          addNewNoteButtonEvent: (AddNewNoteButtonEvent e) async* {
              yield state.copyWith(
                isAddNewNoteButtonClickedState: e.isClickedEvent,
              );
          },
          radioEvent: (RadioEvent e) async* {
            yield state.copyWith(radioGroupState: e.value);
          },
          textInputEvent: (TextInputEvent e) async* {
            yield state.copyWith(textInputState: Note(value: e.value));
          },
        );
      }
    }
    

    the event is:

    class NoteEvent with _$NoteEvent {
      const factory NoteEvent.addNewNoteButtonEvent(
          {required bool isClickedEvent}) = AddNewNoteButtonEvent;
      const factory NoteEvent.radioEvent({required int value}) = RadioEvent;
      const factory NoteEvent.textInputEvent({required String value}) =
          TextInputEvent;
    }
    

    the state is:

    class NoteState with _$NoteState {
      const factory NoteState({
        // required bool showSaveIconState,
        required int radioGroupState,
        required bool isAddNewNoteButtonClickedState,
        required Note textInputState,
      }) = _NoteState;
      // initialize every state
      factory NoteState.initial() => NoteState(
            radioGroupState: 1,
            isAddNewNoteButtonClickedState: false,
            textInputState: Note(value: ''),
          );
    }
    

    the home page code is:

    class HomePage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
        return BlocConsumer<NoteBloc, NoteState>(
          listener: (context, state) {
            // if (state is AddNoteClickedState) {
            if (state.isAddNewNoteButtonClickedState) {
              
              // show navigator
              _scaffoldKey.currentState!
                  .showBottomSheet((context) => const AddNewNoteBottomSheet())
                  .closed
                  .then(
                (value) {
                  // Change state
                  // to tell the bloc that
                  // the bottom sheet is closed
                  context.read<NoteBloc>().add(
                        const NoteEvent.addNewNoteButtonEvent(
                          isClickedEvent: false, // !state.isAddNewNoteClickedState,
                        ),
                      );
                },
              );
            }
          },
          builder: (context, state) {
            return DefaultTabController(
              length: 3,
              child: Scaffold(
                key: _scaffoldKey,
                // add note float action button
                floatingActionButton: InkWell(
                  onTap: () {
                    // make button clicked
                    // to open the bottom sheet
                    context.read<NoteBloc>().add(
                          const NoteEvent.addNewNoteButtonEvent(
                            isClickedEvent: true, //!state.isAddNewNoteClickedState,
                          ),
                        );
                    // print(state);
                  },
                  // for float action button
                  // icon shape
                  child: Container(
                    clipBehavior: Clip.antiAliasWithSaveLayer,
                    height: 11.h,
                    width: 10.h,
                    decoration: BoxDecoration(
                      shape: BoxShape.rectangle,
                      borderRadius: BorderRadius.all(Radius.circular(1.w)),
                      color: const Color(accentColor),
                    ),
                    // add note icon
                    child: Icon(
                      state.isAddNewNoteButtonClickedState ? Icons.save : Icons.add,
                      color: const Color(whiteColor),
                      size: 9.h,
                    ),
                  ),
            );
          },
        );
      }
    }
    

    AddNewNoteBottomSheet code is:

    // For adding new note
    class AddNewNoteBottomSheet extends StatelessWidget {
      const AddNewNoteBottomSheet();
    
      @override
      Widget build(BuildContext context) {
        TextEditingController _textEditingController = TextEditingController();
        return Container(
          // Contains all
          // bottom sheet components
          child: Column(
                  children: [
              // bottom sheet body
              Expanded(
                child: SingleChildScrollView(
                  child: BlocBuilder<NoteBloc, NoteState>(
                    builder: (context, state) {
                      // print("Helllo ${state.maybeMap(orElse: (){},radioClickState: (stat)=>stat.value)}");
                      return Column(
                        children: [
                          
                          // Today Radio
                          ListTile(
                            title: Text(
                              AppLocalizations.of(context)!.homeTodayTapTitle,
                              style: Theme.of(context)
                                  .tabBarTheme
                                  .unselectedLabelStyle,
                            ),
                            contentPadding: const EdgeInsets.all(0),
                            autofocus: true,
                            leading: Radio<int>(
                              value: 1,
                              //get group value
                              groupValue: state.radioGroupState,
                              onChanged: (value) {
                                // tell bloc i click you
                                context
                                    .read<NoteBloc>()
                                    .add(NoteEvent.radioEvent(value: value!));
                              },
                            ),
                          ),
                          // Tomorrow Radio
                          ListTile(
                            contentPadding: const EdgeInsets.all(0),
                            title: Text(
                              AppLocalizations.of(context)!.homeTomorrowTapTitle,
                              style: Theme.of(context)
                                  .tabBarTheme
                                  .unselectedLabelStyle,
                            ),
                            leading: Radio<int>(
                              value: 2,
                              //get group value
                              groupValue: state.radioGroupState,
                              onChanged: (value) {
                                // tell bloc i click you
                                context.read<NoteBloc>().add(
                                      NoteEvent.radioEvent(value: value!),
                                    );
                              },
                            ),
                          ),
                          // Some Day Radio
                          ListTile(
                            contentPadding: const EdgeInsets.all(0),
                            title: Text(
                              AppLocalizations.of(context)!.homeSomeDayTapTitle,
                              style: Theme.of(context)
                                  .tabBarTheme
                                  .unselectedLabelStyle,
                            ),
                            leading: Radio<int>(
                              value: 3,
                              //get group value
                              groupValue: state.radioGroupState,
    
                              onChanged: (value) {
                                // tell bloc i click you
                                context.read<NoteBloc>().add(
                                      NoteEvent.radioEvent(value: value!),
                                    );
                              },
                            ),
                          ),
                        ],
                      );
                    },
                  ),
                ),
              ),
            ],
          ),
        );
      }
    }