bloc_effects 2.1.0
bloc_effects: ^2.1.0 copied to clipboard
Cubit and Bloc abstractions and Flutter Widget that make it easy to add UI Effects to the BLoC state management.
SingleLiveEvent pattern implementation for Cubit and Bloc
Usage #
Lets take a look at how to use mixin Effects or class CubitWithEffects to dispatch effects from CounterCubit to
a CounterPage and react on them with BlocEffectListener.
ui_effect.dart #
abstract class UiEffect {}
class ShowBottomSheet implements UiEffect {
const ShowBottomSheet({required this.counterValue});
final int counterValue;
}
counter_cubit.dart #
class CounterCubit extends Cubit<int> with Effects<UiEffect> {
CounterCubit() : super(0);
void increment() => emit(state + 1);
void onButtonPressed() => emitEffect(ShowBottomSheet(counterValue: state));
}
or the same with the class extension:
class CounterCubit extends CubitWithEffects<int, UiEffect> {
CounterCubit() : super(0);
void increment() => emit(state + 1);
void onButtonPressed() => emitEffect(ShowBottomSheet(counterValue: state));
}
main.dart #
void main() => runApp(CounterApp());
class CounterApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: BlocProvider(
create: (_) => CounterCubit(),
child: CounterPage(),
),
);
}
}
counter_page.dart #
class CounterPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Counter')),
body: BlocEffectListener<CounterCubit, UiEffect>(
listener: (context, effect) {
if (effect is ShowBottomSheet) {
showBottomSheet<void>(
context: context,
builder: (c) =>
Material(
child: Container(
color: Colors.black12,
height: 150,
),
),
);
}
},
child: const SizedBox(),
),
floatingActionButton: FloatingActionButton(
tooltip: 'Show effect',
child: const Icon(Icons.upload),
onPressed: () => context.read<CounterCubit>().onButtonPressed(),
),
);
}
}
And also BlocWithEffectsObserver can be useful. It extends BlocObserver and add onEffect
callback to base observing features:
app_bloc_observer.dart #
class AppBlocObserver extends BlocWithEffectsObserver {
const AppBlocObserver();
@override
void onBlocEffect(BlocBase<dynamic> bloc, Object? effect) {
super.onBlocEffect(bloc, effect);
debugPrint('${bloc.runtimeType} emitted $effect');
}
// Other BlocObserver overrides.
}
Install the observer before creating any blocs or cubits:
void main() {
Bloc.observer = const AppBlocObserver();
runApp(const CounterApp());
}
BlocEffectConsumer combines BlocBuilder and BlocEffectListener when the
same bloc is used for both state-driven UI and transient effects.
CubitWithEffects and BlocWithEffects implement its combined contract. A
class using the Effects mixin directly can opt in by also implementing
BlocEffectsSource<State, Effect>.
Effects are transient, non-buffered broadcast events. Every active listener receives each emission once. Emissions without active listeners are discarded, and new listeners do not receive past effects. Use one route-scoped listener for exclusive UI actions such as navigation and dialogs.