.claude/skills/asyncredux-state-access/SKILL.md
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.
npx skillsauth add marcglasberg/async_redux asyncredux-state-accessInstall 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.
To access your application state in widgets, first define a BuildContext extension. Add this to your project (typically in a shared file that all widgets can import):
extension BuildContextExtension on BuildContext {
AppState get state => getState<AppState>();
AppState read() => getRead<AppState>();
R select<R>(R Function(AppState state) selector) => getSelect<AppState, R>(selector);
R? event<R>(Evt<R> Function(AppState state) selector) => getEvent<AppState, R>(selector);
}
Replace AppState with your actual state class name.
Grants access to the entire state object. All widgets that use context.state will automatically rebuild whenever the store state changes (any part of it).
Widget build(BuildContext context) {
return Text('Counter: ${context.state.counter}');
}
Retrieves only specific state portions. This is more efficient as it only rebuilds the widget when the selected part of the state changes.
Widget build(BuildContext context) {
var counter = context.select((state) => state.counter);
return Text('Counter: $counter');
}
Retrieves state without triggering rebuilds. Use this in event handlers, initState, or anywhere you need to read state once without subscribing to changes.
void _onButtonPressed() {
var currentCount = context.read().counter;
print('Current count is $currentCount');
}
| Method | Use In | Triggers Rebuilds? | Best For |
|--------|--------|-------------------|----------|
| context.state | build method | Yes, on any state change | Simple widgets or when you need many state properties |
| context.select() | build method | Only when selected part changes | Performance-sensitive widgets |
| context.read() | initState, event handlers, callbacks | No | One-time reads, button handlers |
When you need several pieces of state, you have two options:
Option 1: Multiple select calls
Widget build(BuildContext context) {
var name = context.select((state) => state.user.name);
var email = context.select((state) => state.user.email);
var itemCount = context.select((state) => state.items.length);
// Widget rebuilds only if name, email, or itemCount changes
return Text('$name ($email) - $itemCount items');
}
Option 2: Dart records for combined selection
Widget build(BuildContext context) {
var (name, email) = context.select((state) => (state.user.name, state.user.email));
return Text('$name ($email)');
}
Beyond state access, the context extension provides methods for tracking async action progress:
Widget build(BuildContext context) {
// Check if an action is currently running
if (context.isWaiting(LoadDataAction)) {
return CircularProgressIndicator();
}
// Check if an action failed
if (context.isFailed(LoadDataAction)) {
var exception = context.exceptionFor(LoadDataAction);
return Text('Error: ${exception?.message}');
}
// Show the data
return Text('Data: ${context.state.data}');
}
Available methods:
context.isWaiting(ActionType) - Returns true if the action is in progresscontext.isFailed(ActionType) - Returns true if the action recently failedcontext.exceptionFor(ActionType) - Gets the exception from a failed actioncontext.clearExceptionFor(ActionType) - Manually clears the stored exceptionFor complex selection logic, create a WidgetSelect class to organize reusable selectors:
class WidgetSelect {
final BuildContext context;
WidgetSelect(this.context);
// Getter shortcuts
List<Item> get items => context.select((state) => state.items);
User get currentUser => context.select((state) => state.user);
// Custom finder methods
Item? findById(int id) => context.select(
(state) => state.items.firstWhereOrNull((item) => item.id == id)
);
List<Item> searchByText(String text) => context.select(
(state) => state.items.where((item) => item.name.contains(text)).toList()
);
}
Add it to your BuildContext extension:
extension BuildContextExtension on BuildContext {
AppState get state => getState<AppState>();
// ... other methods ...
WidgetSelect get selector => WidgetSelect(this);
}
Usage in widgets:
Widget build(BuildContext context) {
var user = context.selector.currentUser;
var item = context.selector.findById(42);
return Text('${user.name}: ${item?.name}');
}
Never use context.state inside your selector functions. This defeats the purpose of selective rebuilding:
// WRONG - rebuilds on any state change
var items = context.select((state) => context.state.items.where(...));
// CORRECT - only rebuilds when items change
var items = context.select((state) => state.items.where(...));
Nesting context.select causes errors. Always apply selection at the top level:
// WRONG - will cause errors
var result = context.select((state) =>
context.select((s) => s.items).where(...) // Nested select!
);
// CORRECT
var items = context.select((state) => state.items);
var result = items.where(...);
To observe when widgets rebuild (useful for performance debugging), use a ModelObserver:
var store = Store<AppState>(
initialState: AppState.initialState(),
modelObserver: DefaultModelObserver(),
);
The DefaultModelObserver logs console output showing:
Example output:
Model D:1 R:1 = Rebuild:true, Connector:MyWidgetConnector, Model:MyViewModel{counter: 5}
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.