بنقرة واحدة
consume-effect
Add context.effect() to widgets to consume and react to one-time effects from state
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
Add context.effect() to widgets to consume and react to one-time effects from state
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
Add custom error handling with catchError to a mix() call for logging, error conversion, or suppression
Add internet connectivity checking before executing a Cubit method with optional retry
Add debounce to delay method execution until after a period of inactivity for search or validation
Add EffectQueue to state for triggering multiple sequential one-time UI effects
Add Effect fields to state class for one-time UI notifications like snackbars, navigation, or form clearing
Add error state handling to widgets using context.isFailed() and context.getException()
| name | consume-effect |
| description | Add context.effect() to widgets to consume and react to one-time effects from state |
This skill adds context.effect() to widgets to consume and react to one-time effects from state.
Consumes Effect<T> fields in widgets to:
The state must have an Effect<T> field:
class AppState {
final Effect<String> messageEffect;
AppState({Effect<String>? messageEffect})
: messageEffect = messageEffect ?? Effect.spent();
}
Use context.effect() in the build method:
import 'package:bloc_superpowers/bloc_superpowers.dart';
class MyScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Consume the effect - returns value if new, null if spent
final message = context.effect((MyCubit c) => c.state.messageEffect);
if (message != null) {
// React to the effect
WidgetsBinding.instance.addPostFrameCallback((_) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message)),
);
});
}
return Scaffold(
body: MyContent(),
);
}
}
// For typed Effect<T>
final value = context.effect((CubitType c) => c.state.effectField);
// Returns: T? (value if new, null if spent)
// For untyped Effect
final triggered = context.effect((CubitType c) => c.state.effectField);
// Returns: bool (true if new, false if spent)
Widget build(BuildContext context) {
final message = context.effect((AppCubit c) => c.state.snackbarEffect);
if (message != null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message)),
);
});
}
return MyWidget();
}
Widget build(BuildContext context) {
final route = context.effect((AuthCubit c) => c.state.navigateEffect);
if (route != null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
Navigator.pushReplacementNamed(context, route);
});
}
return LoginForm();
}
Widget build(BuildContext context) {
final dialogData = context.effect((OrderCubit c) => c.state.dialogEffect);
if (dialogData != null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
showDialog(
context: context,
builder: (_) => AlertDialog(
title: Text(dialogData.title),
content: Text(dialogData.message),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('OK'),
),
],
),
);
});
}
return OrderSummary();
}
class ChatInput extends StatefulWidget {
@override
State<ChatInput> createState() => _ChatInputState();
}
class _ChatInputState extends State<ChatInput> {
final _controller = TextEditingController();
@override
Widget build(BuildContext context) {
// Clear text when effect is triggered
final shouldClear = context.effect((ChatCubit c) => c.state.clearInputEffect);
if (shouldClear == true) {
_controller.clear();
}
return TextField(controller: _controller);
}
}
Widget build(BuildContext context) {
final newText = context.effect((FormCubit c) => c.state.setTextEffect);
if (newText != null) {
_controller.text = newText;
}
return TextField(controller: _controller);
}
Widget build(BuildContext context) {
final sound = context.effect((GameCubit c) => c.state.playSoundEffect);
if (sound != null) {
audioPlayer.play(sound);
}
final haptic = context.effect((ButtonCubit c) => c.state.hapticEffect);
if (haptic == true) {
HapticFeedback.mediumImpact();
}
return GameBoard();
}
Widget build(BuildContext context) {
final scrollTo = context.effect((ListCubit c) => c.state.scrollEffect);
if (scrollTo != null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
_scrollController.animateTo(
scrollTo,
duration: const Duration(milliseconds: 300),
curve: Curves.easeOut,
);
});
}
return ListView(controller: _scrollController, ...);
}
Handle multiple effects in the same widget:
Widget build(BuildContext context) {
// Effect 1: Show message
final message = context.effect((MyCubit c) => c.state.messageEffect);
if (message != null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message)),
);
});
}
// Effect 2: Clear form
final shouldClear = context.effect((MyCubit c) => c.state.clearFormEffect);
if (shouldClear == true) {
_formKey.currentState?.reset();
}
// Effect 3: Navigate
final route = context.effect((MyCubit c) => c.state.navigateEffect);
if (route != null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
Navigator.pushNamed(context, route);
});
}
return MyForm(key: _formKey);
}
For effects that trigger dialogs, snackbars, or navigation, use addPostFrameCallback to:
// ❌ Don't do this - can cause errors
if (effect != null) {
showDialog(...); // Called during build!
}
// ✓ Do this - waits for build to complete
if (effect != null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
showDialog(...); // Safe - called after build
});
}
context.effect() returns a value, it returns null on subsequent callsclass OrderScreen extends StatefulWidget {
@override
State<OrderScreen> createState() => _OrderScreenState();
}
class _OrderScreenState extends State<OrderScreen> {
final _scrollController = ScrollController();
@override
Widget build(BuildContext context) {
// Success message
final successMsg = context.effect((OrderCubit c) => c.state.successEffect);
if (successMsg != null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(successMsg),
backgroundColor: Colors.green,
),
);
});
}
// Error message
final errorMsg = context.effect((OrderCubit c) => c.state.errorEffect);
if (errorMsg != null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(errorMsg),
backgroundColor: Colors.red,
),
);
});
}
// Scroll to new item
final scrollToEnd = context.effect((OrderCubit c) => c.state.scrollToEndEffect);
if (scrollToEnd == true) {
WidgetsBinding.instance.addPostFrameCallback((_) {
_scrollController.animateTo(
_scrollController.position.maxScrollExtent,
duration: const Duration(milliseconds: 300),
curve: Curves.easeOut,
);
});
}
// Navigate after order complete
final orderId = context.effect((OrderCubit c) => c.state.navigateToOrderEffect);
if (orderId != null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
Navigator.pushReplacementNamed(context, '/order/$orderId');
});
}
return Scaffold(
body: OrderList(scrollController: _scrollController),
);
}
}
Ask the user: