Claude-skill-registry asyncredux-flutter-hooks

Integrate AsyncRedux with the flutter_hooks package. Covers adding flutter_hooks_async_redux, using the useSelector hook, and combining hooks with AsyncRedux state management.

install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/asyncredux-flutter-hooks" ~/.claude/skills/majiayu000-claude-skill-registry-asyncredux-flutter-hooks && rm -rf "$T"
manifest: skills/data/asyncredux-flutter-hooks/SKILL.md
source content

Overview

The

flutter_hooks_async_redux
package provides a hooks-based API for accessing AsyncRedux state. If you prefer functional components with hooks over the widget-based
StoreConnector
pattern, this package lets you use hooks like
useSelector
and
useDispatch
to interact with the Redux store.

Installation

Add these dependencies to your

pubspec.yaml
:

dependencies:
  flutter_hooks: ^0.21.2
  async_redux: ^24.2.2
  flutter_hooks_async_redux: ^3.1.0

Then run

flutter pub get
.

Core Hooks

useSelector

Selects a part of the state and subscribes to updates. The widget rebuilds when the selected value changes:

String username = useSelector<AppState, String>((state) => state.username);

The

distinct
parameter (default
true
) controls whether the widget rebuilds only when the selected value changes.

Creating a Custom useAppState Hook

For convenience, define a custom hook that's pre-typed for your state:

T useAppState<T>(T Function(AppState state) converter, {bool distinct = true}) =>
    useSelector<AppState, T>(converter, distinct: distinct);

This simplifies state access throughout your app:

// Instead of:
String username = useSelector<AppState, String>((state) => state.username);

// Use:
String username = useAppState((state) => state.username);

useDispatch

Dispatches actions that may change the store state. Works with both sync and async actions:

class MyWidget extends HookWidget {
  @override
  Widget build(BuildContext context) {
    var dispatch = useDispatch();

    return ElevatedButton(
      onPressed: () => dispatch(IncrementAction()),
      child: Text('Increment'),
    );
  }
}

useDispatchAndWait

Dispatches an action and returns a

Future<ActionStatus>
that resolves when the action completes:

class MyWidget extends HookWidget {
  @override
  Widget build(BuildContext context) {
    var dispatchAndWait = useDispatchAndWait();
    var dispatch = useDispatch();

    Future<void> handleSubmit() async {
      // Wait for first action to complete
      await dispatchAndWait(DoThisFirstAction());
      // Then dispatch the second
      dispatch(DoThisSecondAction());
    }

    return ElevatedButton(
      onPressed: handleSubmit,
      child: Text('Submit'),
    );
  }
}

You can also check the action status:

var status = await dispatchAndWait(MyAction());
if (status.isCompletedOk) {
  // Action succeeded
}

useDispatchSync

Enforces synchronous action dispatch. Throws

StoreException
if you attempt to dispatch an async action:

var dispatchSync = useDispatchSync();
dispatchSync(MySyncAction()); // OK
dispatchSync(MyAsyncAction()); // Throws StoreException

Waiting and Error Hooks

useIsWaiting

Checks if an async action is currently being processed:

class MyWidget extends HookWidget {
  @override
  Widget build(BuildContext context) {
    var dispatch = useDispatch();
    var isLoading = useIsWaiting(LoadDataAction);

    return Column(
      children: [
        if (isLoading) CircularProgressIndicator(),
        ElevatedButton(
          onPressed: () => dispatch(LoadDataAction()),
          child: Text('Load'),
        ),
      ],
    );
  }
}

You can check by action type, action instance, or multiple types:

// By action type
var isWaiting = useIsWaiting(MyAction);

// By action instance
var action = MyAction();
dispatch(action);
var isWaiting = useIsWaiting(action);

// Multiple types - true if ANY are in progress
var isWaiting = useIsWaiting([BuyAction, SellAction]);

useIsFailed

Checks if an action has failed:

var isFailed = useIsFailed(MyAction);

if (isFailed) {
  return Text('Something went wrong');
}

useExceptionFor

Retrieves the

UserException
from a failed action:

var exception = useExceptionFor(MyAction);

if (exception != null) {
  return Text(exception.reason ?? 'Unknown error');
}

useClearExceptionFor

Gets a function to clear the exception state for an action:

var clearExceptionFor = useClearExceptionFor();

// Clear exception when user dismisses error
ElevatedButton(
  onPressed: () => clearExceptionFor(MyAction),
  child: Text('Dismiss'),
)

Complete Example

Here's a full example combining multiple hooks:

class UserProfileWidget extends HookWidget {
  @override
  Widget build(BuildContext context) {
    // Select state
    var username = useAppState((state) => state.user.name);
    var email = useAppState((state) => state.user.email);

    // Dispatch hooks
    var dispatch = useDispatch();
    var dispatchAndWait = useDispatchAndWait();

    // Loading and error state
    var isLoading = useIsWaiting(UpdateProfileAction);
    var isFailed = useIsFailed(UpdateProfileAction);
    var exception = useExceptionFor(UpdateProfileAction);
    var clearException = useClearExceptionFor();

    Future<void> handleUpdate() async {
      var status = await dispatchAndWait(UpdateProfileAction());
      if (status.isCompletedOk) {
        // Show success message
      }
    }

    return Column(
      children: [
        Text('Username: $username'),
        Text('Email: $email'),

        if (isLoading)
          CircularProgressIndicator(),

        if (isFailed && exception != null)
          Row(
            children: [
              Text(exception.reason ?? 'Update failed'),
              IconButton(
                icon: Icon(Icons.close),
                onPressed: () => clearException(UpdateProfileAction),
              ),
            ],
          ),

        ElevatedButton(
          onPressed: isLoading ? null : handleUpdate,
          child: Text('Update Profile'),
        ),
      ],
    );
  }
}

Hook Parameters Reference

HookAcceptsReturns
useSelector<St, T>
Converter functionSelected value of type T
useDispatch
NoneDispatch function
useDispatchAndWait
NoneFunction returning
Future<ActionStatus>
useDispatchSync
NoneSync dispatch function
useIsWaiting
Action type, instance, or list of types
bool
useIsFailed
Action type, instance, or list of types
bool
useExceptionFor
Action type, instance, or list of types
UserException?
useClearExceptionFor
NoneClear function

Hooks vs StoreConnector

Choose hooks when:

  • You prefer functional widget patterns
  • You're already using
    flutter_hooks
    in your project
  • You want concise state access without view-model boilerplate

Choose

StoreConnector
when:

  • You want explicit separation between UI and state logic
  • You need the structured view-model pattern for testing
  • You're not using hooks elsewhere in your project

Both approaches work well with AsyncRedux - pick the one that fits your team's preferences.

References

URLs from the documentation: