원클릭으로
consume-effect-queue
Add context.effectQueue() to widgets to consume and process queued effects in sequence
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Add context.effectQueue() to widgets to consume and process queued effects in sequence
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
| name | consume-effect-queue |
| description | Add context.effectQueue() to widgets to consume and process queued effects in sequence |
This skill adds context.effectQueue() to widgets to consume and process queued effects in order.
Consumes EffectQueue<T> in widgets to:
The state must have an EffectQueue<T> field with defined effect types:
sealed class UiEffect {}
class ShowToast extends UiEffect { final String message; ShowToast(this.message); }
class Navigate extends UiEffect { final String route; Navigate(this.route); }
class AppState {
final EffectQueue<UiEffect> effectQueue;
AppState({EffectQueue<UiEffect>? effectQueue})
: effectQueue = effectQueue ?? EffectQueue.spent();
}
Use context.effectQueue() in the build method:
import 'package:bloc_superpowers/bloc_superpowers.dart';
class MyScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
context.effectQueue<MyCubit, UiEffect>(
// 1. Select the queue from state
(cubit) => cubit.state.effectQueue,
// 2. Handle each effect type
(context, effect) => switch (effect) {
ShowToast(:final message) =>
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message)),
),
Navigate(:final route) =>
Navigator.pushNamed(context, route),
},
);
return Scaffold(
body: MyContent(),
);
}
}
context.effectQueue<CubitType, EffectType>(
// Selector: extract queue from state
(cubit) => cubit.state.effectQueue,
// Handler: process each effect
(context, effect) => /* handle effect */,
// Optional: execution mode
onePerFrame: true, // Default: true
);
Effects process one at a time, with a rebuild between each:
context.effectQueue<MyCubit, UiEffect>(
(c) => c.state.effectQueue,
onePerFrame: true, // Default - sequential with rebuilds
(context, effect) => handleEffect(effect),
);
Good for:
All effects process in a single frame:
context.effectQueue<MyCubit, UiEffect>(
(c) => c.state.effectQueue,
onePerFrame: false, // All effects execute immediately
(context, effect) => handleEffect(effect),
);
Good for:
Use Dart 3 pattern matching to handle different effect types:
(context, effect) => switch (effect) {
// Destructure properties
ShowToast(:final message) =>
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message)),
),
// Multiple properties
ShowDialog(:final title, :final content) =>
showDialog(
context: context,
builder: (_) => AlertDialog(
title: Text(title),
content: Text(content),
),
),
// No properties
ClearForm() =>
_formKey.currentState?.reset(),
// Navigate with dynamic route
Navigate(:final route) =>
Navigator.pushNamed(context, route),
// Complex handling
ShowConfirmation(:final message, :final onConfirm, :final onCancel) =>
showDialog(
context: context,
builder: (_) => AlertDialog(
content: Text(message),
actions: [
TextButton(onPressed: onCancel, child: Text('Cancel')),
TextButton(onPressed: onConfirm, child: Text('Confirm')),
],
),
),
},
sealed class OnboardingEffect {}
class WelcomeDialog extends OnboardingEffect {}
class PermissionRequest extends OnboardingEffect { final String permission; ... }
class TutorialOverlay extends OnboardingEffect {}
class NavigateToHome extends OnboardingEffect {}
// In widget
context.effectQueue<OnboardingCubit, OnboardingEffect>(
(c) => c.state.effectQueue,
onePerFrame: true, // Show one step at a time
(context, effect) => switch (effect) {
WelcomeDialog() => showWelcomeDialog(context),
PermissionRequest(:final permission) => requestPermission(permission),
TutorialOverlay() => showTutorial(context),
NavigateToHome() => Navigator.pushReplacementNamed(context, '/home'),
},
);
sealed class FormEffect {}
class ShowSaving extends FormEffect {}
class HideSaving extends FormEffect {}
class ShowSuccess extends FormEffect { final String message; ... }
class ClearForm extends FormEffect {}
class NavigateBack extends FormEffect {}
// In widget
context.effectQueue<FormCubit, FormEffect>(
(c) => c.state.effectQueue,
(context, effect) => switch (effect) {
ShowSaving() => _showLoadingOverlay(),
HideSaving() => _hideLoadingOverlay(),
ShowSuccess(:final message) =>
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message), backgroundColor: Colors.green),
),
ClearForm() => _formKey.currentState?.reset(),
NavigateBack() => Navigator.pop(context),
},
);
sealed class ErrorEffect {}
class LogError extends ErrorEffect { final Object error; ... }
class ShowErrorMessage extends ErrorEffect { final String message; ... }
class OfferRetry extends ErrorEffect { final VoidCallback onRetry; ... }
// In widget
context.effectQueue<DataCubit, ErrorEffect>(
(c) => c.state.effectQueue,
(context, effect) => switch (effect) {
LogError(:final error) => analytics.logError(error),
ShowErrorMessage(:final message) =>
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message), backgroundColor: Colors.red),
),
OfferRetry(:final onRetry) =>
showDialog(
context: context,
builder: (_) => AlertDialog(
title: Text('Error'),
content: Text('Would you like to retry?'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('Cancel'),
),
TextButton(
onPressed: () {
Navigator.pop(context);
onRetry();
},
child: Text('Retry'),
),
],
),
),
},
);
// Effects
sealed class CheckoutEffect {}
class ShowProgress extends CheckoutEffect {
final String message;
ShowProgress(this.message);
}
class HideProgress extends CheckoutEffect {}
class ShowSuccess extends CheckoutEffect {
final String orderId;
ShowSuccess(this.orderId);
}
class SendReceipt extends CheckoutEffect {
final String email;
SendReceipt(this.email);
}
class NavigateToOrder extends CheckoutEffect {
final String orderId;
NavigateToOrder(this.orderId);
}
// Widget
class CheckoutScreen extends StatefulWidget {
@override
State<CheckoutScreen> createState() => _CheckoutScreenState();
}
class _CheckoutScreenState extends State<CheckoutScreen> {
OverlayEntry? _progressOverlay;
void _showProgress(String message) {
_progressOverlay = OverlayEntry(
builder: (_) => Material(
color: Colors.black54,
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
CircularProgressIndicator(),
SizedBox(height: 16),
Text(message, style: TextStyle(color: Colors.white)),
],
),
),
),
);
Overlay.of(context).insert(_progressOverlay!);
}
void _hideProgress() {
_progressOverlay?.remove();
_progressOverlay = null;
}
@override
Widget build(BuildContext context) {
context.effectQueue<CheckoutCubit, CheckoutEffect>(
(c) => c.state.effectQueue,
onePerFrame: true,
(context, effect) => switch (effect) {
ShowProgress(:final message) => _showProgress(message),
HideProgress() => _hideProgress(),
ShowSuccess(:final orderId) =>
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Order $orderId placed successfully!'),
backgroundColor: Colors.green,
),
),
SendReceipt(:final email) =>
emailService.sendReceipt(email),
NavigateToOrder(:final orderId) =>
Navigator.pushReplacementNamed(context, '/order/$orderId'),
},
);
return Scaffold(
appBar: AppBar(title: Text('Checkout')),
body: CheckoutForm(),
);
}
}
Ask the user:
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()