side_effect_cubit 2.0.0
side_effect_cubit: ^2.0.0 copied to clipboard
"An extension to the bloc state management library which serve an additional stream for events that should be consumed only once"
Side Effect Cubit #
An extension to the bloc state management library which serve an additional stream for events that should be consumed only once - as usual called one-time events.
Support test package: side_effect_cubit_test
Why Side Effects? #
In modern state management (like Bloc or Cubit), State represents the persistent UI data (e.g., "Loading", "Loaded with [Data]", "Error"). The UI rebuilds whenever the state changes.
However, some actions are one-time events that shouldn't persist or trigger a UI rebuild in the traditional sense. These are called Side Effects.
Examples include:
- Navigating to a new screen.
- Showing a SnackBar, Toast, or Dialog.
- Playing a sound.
- Logging an analytics event.
If you handle these as part of the State:
- Re-triggering issues: If the screen rebuilds (e.g., keyboard opens, rotation), the "Show Error" state might re-trigger the error dialog/snackbar again.
- Boilerplate: You often have to dispatch a "Reset" event immediately after consuming the state to prevent it from happening again.
Side Effect Cubit solves this by providing a separate stream specifically for these one-off actions, decoupling them from your persistent state.
Usage #
1. Adding to existing one #
For Bloc:
class FeatureBloc extends Bloc<FeatureEvent, FeatureState>
with SideEffectBlocMixin<FeatureEvent, FeatureState, FeatureSideEffect> {
FeatureBloc() : super(FeatureState.initial());
}
For Cubit:
class FeatureCubit extends Cubit<FeatureState>
with SideEffectCubitMixin<FeatureState, FeatureSideEffect> {
FeatureCubit() : super(FeatureState.initial());
}
2. Inherit #
For Bloc:
class FeatureBloc extends SideEffectBloc<FeatureEvent, FeatureState, FeatureSideEffect> {
FeatureBloc() : super(FeatureState.initial());
}
For Cubit:
class FeatureCubit extends SideEffectCubit<FeatureState, FeatureSideEffect> {
FeatureCubit() : super(FeatureState.initial());
}
Emit side effect #
class FeatureBloc extends SideEffectBloc<FeatureEvent, FeatureState, FeatureSideEffect> {
FeatureBloc() : super(FeatureState.initial()) {
on<ItemClick>(
(event, emit) {
produceSideEffect(FeatureSideEffect.openItem(event.id));
},
);
}
}
class FeatureCubit extends SideEffectCubit<FeatureState, FeatureSideEffect> {
FeatureCubit() : super(FeatureState.initial());
Future<void> openItem(int id) async {
produceSideEffect(FeatureSideEffect.openItem(id));
}
}
Listen side effect #
BlocSideEffectListener<FeatureBloc, FeatureSideEffect>(
listener: (BuildContext context, FeatureSideEffect sideEffect) {
sideEffect.when(
goToNextScreen: () => Navigator.of(context).pushNamed("/second_screen"),
showPopupError: (errMsg) {
// ....
}
);
},
child: ...
)
BlocSideEffectConsumer<FeatureBloc, FeatureState, FeatureSideEffect>(
sideEffectListener: (BuildContext context, FeatureSideEffect sideEffect) {
// Handle side effects here
},
listenWhen: (previous, current) {
// Return true/false to determine when to call listener
return true;
},
listener: (context, state) {
// Regular Bloc listener
},
buildWhen: (previous, current) => true,
builder: (BuildContext context, FeatureState state) {
return ...
}
)