ワンクリックで
simplify-state
Remove isLoading and errorMessage fields from state classes and use context.isWaiting() and context.isFailed() instead
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Remove isLoading and errorMessage fields from state classes and use context.isWaiting() and context.isFailed() instead
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 | simplify-state |
| description | Remove isLoading and errorMessage fields from state classes and use context.isWaiting() and context.isFailed() instead |
This skill removes isLoading and errorMessage fields from state classes and updates widgets to use context.isWaiting() and context.isFailed() instead.
copyWith methodLook for state classes that have any of these fields:
isLoading, loading, isProcessingerrorMessage, error, errorText, failurestatus enums like StateStatus.loading, StateStatus.errorBefore:
class ProductState {
final List<Product> products;
final bool isLoading;
final String? errorMessage;
final Product? selectedProduct;
final bool isSaving;
const ProductState({
this.products = const [],
this.isLoading = false,
this.errorMessage,
this.selectedProduct,
this.isSaving = false,
});
ProductState copyWith({
List<Product>? products,
bool? isLoading,
String? errorMessage,
Product? selectedProduct,
bool? isSaving,
}) {
return ProductState(
products: products ?? this.products,
isLoading: isLoading ?? this.isLoading,
errorMessage: errorMessage ?? this.errorMessage,
selectedProduct: selectedProduct ?? this.selectedProduct,
isSaving: isSaving ?? this.isSaving,
);
}
}
After:
class ProductState {
final List<Product> products;
final Product? selectedProduct;
const ProductState({
this.products = const [],
this.selectedProduct,
});
ProductState copyWith({
List<Product>? products,
Product? selectedProduct,
}) {
return ProductState(
products: products ?? this.products,
selectedProduct: selectedProduct ?? this.selectedProduct,
);
}
}
Remove all loading/error state emissions from the Cubit:
Before:
class ProductCubit extends Cubit<ProductState> {
ProductCubit() : super(const ProductState());
Future<void> loadProducts() async {
emit(state.copyWith(isLoading: true, errorMessage: null));
try {
final products = await api.getProducts();
emit(state.copyWith(products: products, isLoading: false));
} catch (e) {
emit(state.copyWith(isLoading: false, errorMessage: e.toString()));
}
}
Future<void> saveProduct(Product product) async {
emit(state.copyWith(isSaving: true));
try {
await api.saveProduct(product);
emit(state.copyWith(isSaving: false));
} catch (e) {
emit(state.copyWith(isSaving: false, errorMessage: e.toString()));
}
}
}
After:
import 'package:bloc_superpowers/bloc_superpowers.dart';
enum ProductAction { load, save }
class ProductCubit extends Cubit<ProductState> {
ProductCubit() : super(const ProductState());
void loadProducts() => mix(
key: ProductAction.load,
() async {
final products = await api.getProducts();
emit(state.copyWith(products: products));
},
);
void saveProduct(Product product) => mix(
key: ProductAction.save,
() async {
await api.saveProduct(product);
},
);
}
Before:
Widget build(BuildContext context) {
final state = context.watch<ProductCubit>().state;
if (state.isLoading) {
return const Center(child: CircularProgressIndicator());
}
// ... rest of widget
}
After:
Widget build(BuildContext context) {
if (context.isWaiting(ProductAction.load)) {
return const Center(child: CircularProgressIndicator());
}
final state = context.watch<ProductCubit>().state;
// ... rest of widget
}
Before:
Widget build(BuildContext context) {
final state = context.watch<ProductCubit>().state;
if (state.errorMessage != null) {
return Column(
children: [
Text('Error: ${state.errorMessage}'),
ElevatedButton(
onPressed: () => context.read<ProductCubit>().loadProducts(),
child: const Text('Retry'),
),
],
);
}
// ... rest of widget
}
After:
Widget build(BuildContext context) {
if (context.isFailed(ProductAction.load)) {
return Column(
children: [
Text('Error: ${context.getException(ProductAction.load)}'),
ElevatedButton(
onPressed: () => context.read<ProductCubit>().loadProducts(),
child: const Text('Retry'),
),
],
);
}
final state = context.watch<ProductCubit>().state;
// ... rest of widget
}
Before:
ElevatedButton(
onPressed: state.isSaving ? null : () => cubit.saveProduct(product),
child: state.isSaving
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Text('Save'),
)
After:
ElevatedButton(
onPressed: context.isWaiting(ProductAction.save)
? null
: () => context.read<ProductCubit>().saveProduct(product),
child: context.isWaiting(ProductAction.save)
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Text('Save'),
)
When all methods share the same loading state:
// Cubit
void loadProducts() => mix(key: this, () async { ... });
void refreshProducts() => mix(key: this, () async { ... });
// Widget
if (context.isWaiting(ProductCubit)) { ... }
When different operations need separate loading states:
enum ProductAction { load, save, delete }
// Cubit
void loadProducts() => mix(key: ProductAction.load, () async { ... });
void saveProduct(p) => mix(key: ProductAction.save, () async { ... });
void deleteProduct(id) => mix(key: ProductAction.delete, () async { ... });
// Widget
if (context.isWaiting(ProductAction.load)) { ... } // Loading products
if (context.isWaiting(ProductAction.save)) { ... } // Saving
When you need loading states per item:
// Cubit
void deleteProduct(String id) => mix(
key: (ProductAction.delete, id),
() async { ... },
);
// Widget - show spinner on specific item being deleted
if (context.isWaiting((ProductAction.delete, product.id))) {
return const CircularProgressIndicator();
}
| Extension | Purpose | Example |
|---|---|---|
context.isWaiting(key) | Check if operation is in progress | context.isWaiting(UserCubit) |
context.isFailed(key) | Check if operation failed | context.isFailed(UserCubit) |
context.getException(key) | Get the exception that was thrown | context.getException(UserCubit) |
context.clearException(key) | Manually clear the error state | context.clearException(UserCubit) |
For each state class:
isLoading / loading / isProcessing fieldserrorMessage / error / failure fieldsstatus enum if only used for loading/errorcopyWith methodFor each Cubit:
import 'package:bloc_superpowers/bloc_superpowers.dart';mix()throw UserException()For each widget:
state.isLoading with context.isWaiting(key)state.errorMessage != null with context.isFailed(key)state.errorMessage with context.getException(key)