Here I have a _buildFrontCardDisplaySetting
which is used to build a list of checkbox to determine which field should be displayed. Each widget in a list is wrapped by a BlocBuilder
, and in the widget is a CheckBoxListTile
. In the CheckBoxListTile
, its value will set to true if the setting option, which is bound to the checkbox, in the list setting in the state class.
My problem is: the UI doesn't react to change if I click the checkbox. I have debugged the code, and it does jump to the copyWith
method in the class state, but the bloc class doesn't emit any state. I am struggling to find the root cause for my issue. If anyone have an idea here?. Below is my video screenshot and the code:
@override
Widget build(BuildContext context) {
return BlocListener<FlashCardSettingCubit, FlashCardSettingState>(
listener: (context, state) {
...
},
child: SimpleDialog(
children: [
..._buildFrontCardDisplaySetting(context),
],
),
);
};
List<Widget> _buildFrontCardDisplaySetting(BuildContext context) {
final list = FrontSideDisplaySetting.values.map((e) {
return BlocBuilder<FlashCardSettingCubit, FlashCardSettingState>(
buildWhen: (prev, current){
return prev.frontSidesSetting != current.frontSidesSetting;
},
builder: (context, state) {
return CheckboxListTile(
key: ValueKey(e),
title: Text(context.frontSetting(e)),
value: state.frontSidesSetting.contains(e),
onChanged: (value) {
context.read<FlashCardSettingCubit>().chooseFrontCardSetting(e);
});
},
);
}).toList();
return list;
}
class FlashCardSettingCubit extends Cubit<FlashCardSettingState> {
FlashCardSettingCubit() : super(FlashCardSettingState.initial());
void chooseFrontCardSetting(FrontSideDisplaySetting setting) {
final isContain = state.frontSidesSetting.contains(setting);
if (isContain) {
state.frontSidesSetting.remove(setting);
} else {
state.frontSidesSetting.add(setting);
}
emit(state.copyWith(frontSidesSetting: List.from(state.frontSidesSetting)));
}
void chooseBackCardSetting(BackSideDisplaySetting setting) {
final isContain = state.backSidesSetting.contains(setting);
if (isContain) {
state.backSidesSetting.remove(setting);
} else {
state.backSidesSetting.add(setting);
}
emit(state.copyWith(backSidesSetting: List.from(state.backSidesSetting)));
}
}
enum FlashCardSettingStatus { selected, cancel, initial }
class FlashCardSettingState extends Equatable {
final List<FrontSideDisplaySetting> frontSidesSetting;
final List<BackSideDisplaySetting> backSidesSetting;
final FlashCardSettingStatus status;
@override
List<Object> get props => [frontSidesSetting, backSidesSetting, status];
const FlashCardSettingState({
required this.frontSidesSetting,
required this.backSidesSetting,
required this.status,
});
FlashCardSettingState.initial()
: this(
frontSidesSetting: const [],
backSidesSetting: const [],
status: FlashCardSettingStatus.initial);
FlashCardSettingState copyWith({
List<FrontSideDisplaySetting>? frontSidesSetting,
List<BackSideDisplaySetting>? backSidesSetting,
FlashCardSettingStatus? status,
}) {
return FlashCardSettingState(
frontSidesSetting: frontSidesSetting ?? this.frontSidesSetting,
backSidesSetting: backSidesSetting ?? this.backSidesSetting,
status: status ?? this.status,
);
}
}
The problem is following line
emit(state.copyWith(frontSidesSetting: List.from(state.frontSidesSetting)));
Here you are emitting same state so cubit does not produce this event. What you want is
final frontSidesSetting = List.from(state.frontSidesSetting);
final isContain = fromSideSetting.contains(setting);
if (isContain) {
frontSidesSetting.remove(setting);
} else {
frontSidesSetting.add(setting);
}
emit(state.copyWith(frontSidesSetting: frontSidesSetting));
This way, the current state frontSidesSetting and value to be emitted will be different.