bottom sheet is showing repeatedly once event triggred using bloc
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.
Winter
Updated on December 01, 2022Comments
-
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!), ); }, ), ), ], ); }, ), ), ), ], ), ); } }