.claude/skills/asyncredux-flutter-hooks/SKILL.md
Integrate AsyncRedux with the flutter_hooks package. Covers adding flutter_hooks_async_redux, using the useSelector hook, and combining hooks with AsyncRedux state management.
npx skillsauth add marcglasberg/async_redux asyncredux-flutter-hooksInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
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.
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.
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.
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);
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'),
);
}
}
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
}
Enforces synchronous action dispatch. Throws StoreException if you attempt to dispatch an async action:
var dispatchSync = useDispatchSync();
dispatchSync(MySyncAction()); // OK
dispatchSync(MyAsyncAction()); // Throws StoreException
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]);
Checks if an action has failed:
var isFailed = useIsFailed(MyAction);
if (isFailed) {
return Text('Something went wrong');
}
Retrieves the UserException from a failed action:
var exception = useExceptionFor(MyAction);
if (exception != null) {
return Text(exception.reason ?? 'Unknown error');
}
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'),
)
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 | Accepts | Returns |
|------|---------|---------|
| useSelector<St, T> | Converter function | Selected value of type T |
| useDispatch | None | Dispatch function |
| useDispatchAndWait | None | Function returning Future<ActionStatus> |
| useDispatchSync | None | Sync 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 | None | Clear function |
Choose hooks when:
flutter_hooks in your projectChoose StoreConnector when:
Both approaches work well with AsyncRedux - pick the one that fits your team's preferences.
URLs from the documentation:
data-ai
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.
data-ai
Use `waitCondition()` inside actions to pause execution until state meets criteria. Covers waiting for price thresholds, coordinating between actions, and implementing conditional workflows.
testing
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.
tools
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.