asyncredux-sync-actions

Creates AsyncRedux (Flutter) synchronous actions that update state immediately by implementing reduce() to return a new state.

16 stars

Best use case

asyncredux-sync-actions is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Creates AsyncRedux (Flutter) synchronous actions that update state immediately by implementing reduce() to return a new state.

Teams using asyncredux-sync-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

$curl -o ~/.claude/skills/asyncredux-sync-actions/SKILL.md --create-dirs "https://raw.githubusercontent.com/diegosouzapw/awesome-omni-skill/main/skills/design/asyncredux-sync-actions/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/asyncredux-sync-actions/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How asyncredux-sync-actions Compares

Feature / Agentasyncredux-sync-actionsStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Creates AsyncRedux (Flutter) synchronous actions that update state immediately by implementing reduce() to return a new state.

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 Sync Actions

## Basic Sync Action Structure

A synchronous action returns `AppState?` from its `reduce()` method. The action completes
immediately and state updates right away.

```dart
class Increment extends ReduxAction<AppState> {
  @override
  AppState? reduce() => state.copy(counter: state.counter + 1);
}
```

## Key Components

### Extending ReduxAction

Every action extends `ReduxAction<AppState>`:

```dart
class MyAction extends ReduxAction<AppState> {
  @override
  AppState? reduce() {
    // Return new state
  }
}
```

### The `state` Getter

Inside `reduce()`, access current state via the `state` getter:

```dart
class ToggleFlag extends ReduxAction<AppState> {
  @override
  AppState? reduce() => state.copy(flag: !state.flag);
}
```

### Passing Parameters via Constructor

Pass data to actions through constructor fields:

```dart
class SetName extends ReduxAction<AppState> {
  final String name;
  SetName(this.name);

  @override
  AppState? reduce() => state.copy(name: name);
}

class IncrementBy extends ReduxAction<AppState> {
  final int amount;
  IncrementBy({required this.amount});

  @override
  AppState? reduce() => state.copy(counter: state.counter + amount);
}
```

### Modifying Nested State

For nested state objects, create the new nested object first:

```dart
class UpdateUserName extends ReduxAction<AppState> {
  final String name;
  UpdateUserName(this.name);

  @override
  AppState? reduce() {
    var newUser = state.user.copy(name: name);
    return state.copy(user: newUser);
  }
}
```

## Dispatching Sync Actions

### From Widgets

Use context extensions:

```dart
// Fire and forget
context.dispatch(Increment());

// With parameters
context.dispatch(SetName('Alice'));
context.dispatch(IncrementBy(amount: 5));
```

### Immediate State Update

Sync actions update state immediately:

```dart
print(store.state.counter); // 2
store.dispatch(IncrementBy(amount: 3));
print(store.state.counter); // 5
```

### Guaranteed Sync with dispatchSync()

The `dispatchSync()` throws `StoreException` if the action is async. Otherwise, it
behaves exactly like `dispatch()`.

Use `dispatchSync()` only in the rare cases when you must ensure the action is synchronous
because you need the state to be applied right after the dispatch returns. 

```dart
context.dispatchSync(Increment());
```

### From Other Actions

Actions can dispatch other actions:

```dart
class ResetAndIncrement extends ReduxAction<AppState> {
  @override
  AppState? reduce() {
    dispatch(Reset());
    dispatch(Increment());
    return null; // This action itself doesn't change state
  }
}
```

## Returning Null (No State Change)

Return `null` when you don't need to change state:

```dart
class LogCurrentState extends ReduxAction<AppState> {
  @override
  AppState? reduce() {
    print('Current counter: ${state.counter}');
    return null; // No state change
  }
}
```

Conditional state changes:

```dart
class IncrementIfPositive extends ReduxAction<AppState> {
  final int amount;
  IncrementIfPositive(this.amount);

  @override
  AppState? reduce() {
    if (amount <= 0) return null;
    return state.copy(counter: state.counter + amount);
  }
}
```

## Action Simplification with Base Class

Create a base action class to reduce boilerplate:

```dart
// Define once
abstract class AppAction extends ReduxAction<AppState> {}

// Use everywhere
class Increment extends AppAction {
  @override
  AppState? reduce() => state.copy(counter: state.counter + 1);
}

class SetName extends AppAction {
  final String name;
  SetName(this.name);

  @override
  AppState? reduce() => state.copy(name: name);
}
```

You can add shared functionality to your base class:

```dart
abstract class AppAction extends ReduxAction<AppState> {
  // Shortcuts to state parts
  User get user => state.user;
  Settings get settings => state.settings;
}

class UpdateEmail extends AppAction {
  final String email;
  UpdateEmail(this.email);

  @override
  AppState? reduce() => state.copy(
    user: user.copy(email: email), // Uses shortcut
  );
}
```

## Return Type Warning

The `reduce()` method signature is `FutureOr<AppState?>`. For sync actions, always return
`AppState?` directly:

```dart
// CORRECT - Sync action
AppState? reduce() => state.copy(counter: state.counter + 1);

// WRONG - Don't return FutureOr directly
FutureOr<AppState?> reduce() => state.copy(counter: state.counter + 1);
```

If you return `FutureOr<AppState?>` directly, AsyncRedux cannot determine if the action is
sync or async and will throw a `StoreException`.

## Complete Example

```dart
// State
class AppState {
  final int counter;
  final String name;

  AppState({required this.counter, required this.name});

  static AppState initialState() => AppState(counter: 0, name: '');

  AppState copy({int? counter, String? name}) => AppState(
    counter: counter ?? this.counter,
    name: name ?? this.name,
  );
}

// Base action
abstract class AppAction extends ReduxAction<AppState> {}

// Sync actions
class Increment extends AppAction {
  @override
  AppState? reduce() => state.copy(counter: state.counter + 1);
}

class Decrement extends AppAction {
  @override
  AppState? reduce() => state.copy(counter: state.counter - 1);
}

class IncrementBy extends AppAction {
  final int amount;
  IncrementBy(this.amount);

  @override
  AppState? reduce() => state.copy(counter: state.counter + amount);
}

class SetName extends AppAction {
  final String name;
  SetName(this.name);

  @override
  AppState? reduce() => state.copy(name: name);
}

class Reset extends AppAction {
  @override
  AppState? reduce() => AppState.initialState();
}

// Usage in widget
ElevatedButton(
  onPressed: () => context.dispatch(IncrementBy(5)),
  child: Text('Add 5'),
)
```

## References

URLs from the documentation:

- https://asyncredux.com/flutter/basics/sync-actions
- https://asyncredux.com/flutter/basics/actions-and-reducers
- https://asyncredux.com/flutter/advanced-actions/redux-action
- https://asyncredux.com/flutter/basics/dispatching-actions
- https://asyncredux.com/flutter/basics/action-simplification
- https://asyncredux.com/flutter/basics/changing-state-is-optional

Related Skills

asyncredux-wait-fail-succeed

16
from diegosouzapw/awesome-omni-skill

Show loading states and handle action failures in widgets. Covers `isWaiting(ActionType)` for spinners, `isFailed(ActionType)` for error states, `exceptionFor(ActionType)` for error messages, and `clearExceptionFor()` to reset failure states.

asyncredux-wait-condition

16
from diegosouzapw/awesome-omni-skill

Use `waitCondition()` inside actions to pause execution until state meets criteria. Covers waiting for price thresholds, coordinating between actions, and implementing conditional workflows.

asyncredux-user-exceptions

16
from diegosouzapw/awesome-omni-skill

Handle user-facing errors with UserException. Covers throwing UserException from actions, setting up UserExceptionDialog, customizing error dialogs with `onShowUserExceptionDialog`, and using UserExceptionAction for non-interrupting error display.

asyncredux-streams-timers

16
from diegosouzapw/awesome-omni-skill

Manage Streams and Timers with AsyncRedux. Covers creating actions to start/stop streams, storing stream subscriptions in store props, dispatching actions from stream callbacks, and proper cleanup with disposeProps().

asyncredux-state-access

16
from diegosouzapw/awesome-omni-skill

Access store state in widgets using `context.state`, `context.select()`, and `context.read()`. Covers when to use each method, setting up BuildContext extensions, and optimizing widget rebuilds with selective state access.

asyncredux-setup

16
from diegosouzapw/awesome-omni-skill

Initialize, setup and configure AsyncRedux in a Flutter app. Use it whenever starting a new AsyncRedux project, or when the user requests.

asyncredux-selectors

16
from diegosouzapw/awesome-omni-skill

Create and cache selectors for efficient state access. Covers writing selector functions, caching with `cache1` and `cache2`, the reselect pattern, and avoiding repeated computations in widgets.

asyncredux-persistence

16
from diegosouzapw/awesome-omni-skill

Implement local state persistence using Persistor. Covers creating a custom Persistor class, implementing `readState()`, `persistDifference()`, `deleteState()`, using LocalPersist helper, throttling saves, and pausing/resuming persistence with app lifecycle.

asyncredux-optimistic-update-mixin

16
from diegosouzapw/awesome-omni-skill

Add the OptimisticUpdate mixin for instant UI feedback before server confirmation. Covers immediate state changes, automatic rollback on failure, and optionally notifying users of rollback.

asyncredux-navigation

16
from diegosouzapw/awesome-omni-skill

Handle navigation through actions using NavigateAction. Covers setting up the navigator key, dispatching NavigateAction for push/pop/replace, and testing navigation in isolation.

asyncredux-events

16
from diegosouzapw/awesome-omni-skill

Use the Event class to interact with Flutter's stateful widgets (TextField, ListView, etc.). Covers creating Event objects in state, consuming events with `context.event()`, scrolling lists, changing text fields, and the event lifecycle.

asyncredux-dependency-injection

16
from diegosouzapw/awesome-omni-skill

Inject dependencies into actions using the environment pattern. Covers creating an Environment class, passing it to the Store, accessing `env` from actions, and using dependency injection for testability.