asyncredux-async-actions
Creates AsyncRedux (Flutter) asynchronous actions for API calls, database operations, and other async work.
Best use case
asyncredux-async-actions is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Creates AsyncRedux (Flutter) asynchronous actions for API calls, database operations, and other async work.
Teams using asyncredux-async-actions should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/asyncredux-async-actions/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How asyncredux-async-actions Compares
| Feature / Agent | asyncredux-async-actions | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Creates AsyncRedux (Flutter) asynchronous actions for API calls, database operations, and other async work.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# AsyncRedux Async Actions
## Basic Async Action Structure
An action becomes asynchronous when its `reduce()` method returns `Future<AppState?>`
instead of `AppState?`. Use this for database access, API calls, file operations, or any
work requiring `await`.
```dart
class FetchUser extends ReduxAction<AppState> {
@override
Future<AppState?> reduce() async {
final user = await api.fetchUser();
return state.copy(user: user);
}
}
```
Unlike traditional Redux requiring middleware, AsyncRedux makes it simple: return a
`Future` and it works.
## Critical Rule: Every Path Must Have await
If the action is async (returns a Future) and changes the state (returns a non-null
state), the framework requires that, **all execution paths contain at least
one `await`**. Never declare `Future<AppState?>` if you don't actually await something.
### Valid Patterns
```dart
// Simple async with await
Future<AppState?> reduce() async {
final data = await fetchData();
return state.copy(data: data);
}
// Using microtask (minimum valid await)
Future<AppState?> reduce() async {
await microtask;
return state.copy(timestamp: DateTime.now());
}
// Conditional - both paths have await
Future<AppState?> reduce() async {
if (state.needsRefresh) {
return await fetchAndUpdate();
}
else return await validateCurrent();
}
// Always returns null
Future<AppState?> reduce() async {
if (state.needsRefresh) {
await fetchAndUpdate();
}
return null;
}
```
### Invalid Patterns (Will Cause Issues)
```dart
// WRONG: No await at all
Future<AppState?> reduce() async {
return state.copy(counter: state.counter + 1);
}
// WRONG: await only on some paths
Future<AppState?> reduce() async {
if (condition) {
return await fetchData();
}
return state; // No await on this path!
}
// WRONG: Calling async function without await
Future<AppState?> reduce() async {
someAsyncFunction(); // Not awaited
return state;
}
```
## Using assertUncompletedFuture()
For complex reducers with multiple code paths, add `assertUncompletedFuture()` before the
final return. This catches violations at runtime during development:
```dart
class ComplexAction extends ReduxAction<AppState> {
@override
Future<AppState?> reduce() async {
if (state.cacheValid) {
// Complex logic that might accidentally skip await
return processCache();
}
final data = await fetchFromServer();
final processed = transform(data);
assertUncompletedFuture(); // Validates at least one await occurred
return state.copy(data: processed);
}
}
```
## State Changes During Async Operations
The `state` getter can change after every `await` because other actions may modify state
while yours is waiting:
```dart
class AsyncAction extends ReduxAction<AppState> {
@override
Future<AppState?> reduce() async {
print(state.counter); // e.g., 5
await someSlowOperation();
// state.counter might now be different (e.g., 10)
// if another action modified it during the await
print(state.counter);
return state.copy(counter: state.counter + 1);
}
}
```
### Using initialState for Comparison
Use `initialState` to access the state as it was when the action was dispatched (never
changes):
```dart
class SafeIncrement extends ReduxAction<AppState> {
@override
Future<AppState?> reduce() async {
final originalCounter = initialState.counter;
await validateWithServer();
// Check if state changed while we were waiting
if (state.counter != originalCounter) {
// State was modified by another action
return null; // Abort our change
}
return state.copy(counter: state.counter + 1);
}
}
```
## Dispatching Async Actions
### Fire and Forget
Use `dispatch()` when you don't need to wait for completion:
```dart
context.dispatch(FetchUser());
// Returns immediately, action runs in background
```
### Wait for Completion
Use `dispatchAndWait()` to await the action's completion:
```dart
await context.dispatchAndWait(FetchUser());
// Continues only after action finishes AND state changes
print('User loaded: ${context.state.user.name}');
```
### Dispatch Multiple in Parallel
```dart
// Fire all, don't wait
context.dispatchAll([FetchUser(), FetchSettings(), FetchNotifications()]);
// Fire all and wait for all to complete
await context.dispatchAndWaitAll([FetchUser(), FetchSettings()]);
```
## Showing Loading States
Use `isWaiting()` to show spinners while async actions run:
```dart
Widget build(BuildContext context) {
if (context.isWaiting(FetchUser)) return CircularProgressIndicator();
else return Text('Hello, ${context.state.user.name}');
}
```
## Error Handling
Throw `UserException` for user-facing errors:
```dart
class FetchUser extends ReduxAction<AppState> {
@override
Future<AppState?> reduce() async {
final response = await api.fetchUser();
if (response.statusCode == 404)
throw UserException('User not found.');
if (response.statusCode != 200)
throw UserException('Failed to load user. Please try again.');
return state.copy(user: response.data);
}
}
```
Check for failures in widgets:
```dart
Widget build(BuildContext context) {
if (context.isFailed(FetchUser)) {
return Text('Error: ${context.exceptionFor(FetchUser)?.message}');
}
// ...
}
```
## Complete Example
```dart
// Async action with proper error handling
class LoadProducts extends ReduxAction<AppState> {
@override
Future<AppState?> reduce() async {
try {
final products = await api.fetchProducts();
return state.copy(products: products, productsLoaded: true);
} catch (e) {
throw UserException('Could not load products. Check your connection.');
}
}
}
// Widget showing all three states
Widget build(BuildContext context) {
// Loading state
if (context.isWaiting(LoadProducts)) {
return Center(child: CircularProgressIndicator());
}
// Error state
if (context.isFailed(LoadProducts)) {
return Center(
child: Column(
children: [
Text(context.exceptionFor(LoadProducts)?.message ?? 'Error'),
ElevatedButton(
onPressed: () => context.dispatch(LoadProducts()),
child: Text('Retry'),
),
],
),
);
}
// Success state
return ListView.builder(
itemCount: context.state.products.length,
itemBuilder: (_, i) => ProductTile(context.state.products[i]),
);
}
```
## Return Type Warning
Never return `FutureOr<AppState?>` directly. AsyncRedux must know if the action is sync or
async:
```dart
// CORRECT
Future<AppState?> reduce() async { ... }
// CORRECT
AppState? reduce() { ... }
// WRONG - throws StoreException
FutureOr<AppState?> reduce() { ... }
```
## References
URLs from the documentation:
- https://asyncredux.com/flutter/basics/async-actions
- https://asyncredux.com/flutter/basics/actions-and-reducers
- https://asyncredux.com/flutter/advanced-actions/redux-action
- https://asyncredux.com/flutter/basics/failed-actions
- https://asyncredux.com/flutter/basics/dispatching-actions
- https://asyncredux.com/flutter/basics/wait-fail-succeedRelated Skills
asynchronous-programming-preference
Favors the use of async and await for asynchronous programming in Python.
async-operations
Specifies the preferred syntax for asynchronous operations using async/await and onMount for component initialization. This results in cleaner and more readable asynchronous code.
python-github-actions
Complete Python GitHub Actions system. PROACTIVELY activate for: (1) uv-based CI workflows (10-100x faster), (2) Matrix testing across Python versions, (3) Dependency caching with setup-uv, (4) Parallel test execution, (5) Reusable workflows, (6) Publishing to PyPI with trusted publishing, (7) Code coverage with codecov, (8) Security scanning. Provides: Workflow templates, caching config, matrix strategies, composite actions. Ensures fast, reliable CI/CD pipelines.
async-repl-protocol
Async REPL Protocol
form-and-actions-in-sveltekit
Describes Form and Actions implementations.
asyncredux-undo-redo
Implement undo/redo functionality using state observers. Covers recording state history with stateObserver, creating a RecoverStateAction, implementing undo for the full state or partial state, and managing history limits.
asyncredux-observers
Set up observers for debugging and monitoring. Covers implementing actionObservers for dispatch logging, stateObserver for state change tracking, combining observers with globalWrapError, and using observers for analytics.
asyncredux-nonreentrant-mixin
Add the NonReentrant mixin to prevent an action from dispatching while already in progress. Covers preventing duplicate form submissions, avoiding race conditions, and protecting long-running operations.
asyncpg-detection
This skill should be used when the user asks to "detect asyncpg usage", "find asyncpg patterns", "scan for asyncpg imports", or "identify asyncpg database code in FastAPI projects". It automatically scans Python files to identify asyncpg imports, connection patterns, and query execution methods that need conversion to SQLAlchemy.
asyncio
Python asyncio - Modern concurrent programming with async/await, event loops, tasks, coroutines, primitives, aiohttp, and FastAPI async patterns
asyncapi-docs
AsyncAPI specification handling for event-driven API documentation. Parse, validate, and generate documentation for message-based APIs including Kafka, MQTT, WebSocket, and AMQP systems.
asyncapi-design
Event-driven API specification with AsyncAPI 3.0 for message-based architectures