bloc_after_effect 0.1.0
bloc_after_effect: ^0.1.0 copied to clipboard
An extension of Bloc that adds a one-shot UI side-effect stream alongside state, for navigation, dialogs, snackbars, and other fire-and-forget actions.
bloc_after_effect #
An extension of bloc that adds a one-shot UI side-effect stream alongside state — for navigation, dialogs, snackbars, and other fire-and-forget UI actions that don't belong in State.
- Bloc decides what happened:
emitEffect(ShowErrorDialog('Save failed')) - UI decides how to show it:
showDialog(context: context, ...)
Install #
flutter pub add bloc_after_effect
Usage #
Define your effects, extend EffectBloc, and emit effects from event handlers:
abstract class ProfileEffect {}
class ShowErrorSnackBar extends ProfileEffect {
ShowErrorSnackBar(this.message);
final String message;
}
class ProfileBloc extends EffectBloc<ProfileEvent, ProfileState, ProfileEffect> {
ProfileBloc() : super(ProfileState.initial()) {
on<SavePressed>((event, emit) async {
try {
await repo.save();
emitEffect(ShowSuccessSnackBar('Saved'));
} catch (_) {
emitEffect(ShowErrorSnackBar('Save failed'));
}
});
}
}
Listen for effects in the widget tree with BlocEffectListener:
BlocEffectListener<ProfileBloc, ProfileEffect>(
listener: (context, effect) {
if (effect is ShowErrorSnackBar) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(effect.message)),
);
} else if (effect is NavigateToEdit) {
Navigator.of(context).push(
MaterialPageRoute(builder: (_) => EditPage(userId: effect.userId)),
);
}
},
child: ProfilePageBody(),
)
If bloc is not passed explicitly, it is resolved via context.read<B>(), so it works with BlocProvider out of the box.
State vs Effect #
| State | Effect | |
|---|---|---|
| Stored | Yes, in the Bloc | No, fire-and-forget |
| Replayed on rebuild | Yes | No |
| Examples | isLoading, items, error |
showDialog, navigate, showSnackBar |
If the UI must render it on every build — it's State. If the UI must do it once — it's an Effect.
The effects stream is a broadcast stream: effects emitted with no active listener are dropped.
Documentation & example #
- Full documentation: doc/bloc_after_effect.md
- Runnable example: example/
License #
MIT — see LICENSE.