ワンクリックで
asyncredux-setup
// Initialize, setup and configure AsyncRedux in a Flutter app. Use it whenever starting a new AsyncRedux project, or when the user requests.
// Initialize, setup and configure AsyncRedux in a Flutter app. Use it whenever starting a new AsyncRedux project, or when the user requests.
Inject dependencies into actions using the environment, dependencies, and configuration pattern. Covers creating an Environment enum, a Dependencies class, passing them to the Store, accessing them from actions and widgets, and using dependency injection for testability.
Create a custom base action class for your app. Covers adding getter shortcuts to state parts, adding selector methods, implementing shared wrapError logic, and establishing project-wide action conventions.
Stops an AsyncRedux (Flutter) action from dispatching. Use only when the user mentions abortDispatch(), or explicitly asks to abort or prevent dispatch under certain conditions.
Checks an AsyncRedux (Flutter) action's completion status using ActionStatus right after the dispatch returns. Use only when you need to know whether an action completed, whether it failed with an error, what error it produced, or how to navigate based on success or failure.
Creates AsyncRedux (Flutter) actions that return null from reduce() to not change the state. Such actions can still do side effects, dispatch other actions, or do nothing.
Creates AsyncRedux (Flutter) asynchronous actions for API calls, database operations, and other async work.
| name | asyncredux-setup |
| description | Initialize, setup and configure AsyncRedux in a Flutter app. Use it whenever starting a new AsyncRedux project, or when the user requests. |
Add AsyncRedux to your pubspec.yaml:
dependencies:
async_redux: ^25.6.1
Check pub.dev for the latest version.
Create an immutable AppState class (in file app_state.dart) with:
copy() method== equals methodhashCode methodinitialState() static factoryIf the app is new, and you don't have any state yet, create an empty AppState:
@immutable
class AppState {
AppState();
static AppState initialState() => AppState();
AppState copy() => AppState();
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is AppState && runtimeType == other.runtimeType;
@override
int get hashCode => 0;
}
If there is existing state, create the AppState that incorporates that state.
This is an example:
@immutable
class AppState {
final String name;
final int age;
AppState({required this.name, required this.age});
static AppState initialState() => AppState(name: "", age: 0);
AppState copy({String? name, int? age}) => AppState(
name: name ?? this.name,
age: age ?? this.age,
);
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is AppState &&
runtimeType == other.runtimeType &&
name == other.name &&
age == other.age;
@override
int get hashCode => Object.hash(name, age);
}
All fields must be final (immutable). Add additional helper methods as needed:
AppState withName(String name) => copy(name: name);
AppState withAge(int age) => copy(age: age);
Find the place where you initialize your app (usually in main.dart),
and import your AppState class (adapt the path as needed) and the AsyncRedux package:
import 'app_state.dart';
import 'package:async_redux/async_redux.dart';
Create the store with your initial state. Note that PersistorDummy,
GlobalWrapErrorDummy, and ConsoleActionObserver are provided by AsyncRedux for basic
setups. In the future these can be replaced with custom implementations as needed.
late Store store;
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Create the persistor, and try to read any previously saved state.
var persistor = PersistorDummy<AppState>();
AppState? initialState = await persistor.readState();
// If there is no saved state, create a new empty one and save it.
if (initialState == null) {
initialState = AppState.initialState();
await persistor.saveInitialState(initialState);
}
// Create the store.
store = Store<AppState>(
initialState: initialState,
persistor: persistor,
globalWrapError: GlobalWrapErrorDummy(),
actionObservers: [ConsoleActionObserver()],
);
runApp(...);
}
Wrap your app with StoreProvider to make the store accessible.
Find the root of the widget tree of the app, and add it above MaterialApp (or
CupertinoApp, adapting as needed). Note you will need to import the store too.
import 'package:async_redux/async_redux.dart';
Widget build(context) {
return StoreProvider<AppState>(
store: store,
child: MaterialApp( ... ),
);
}
You must add this extension to your file containing AppState (this is required for
easier state access in widgets):
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);
}
Create file app_action.dart with this abstract class extending ReduxAction<AppState>:
/// All actions extend this class.
abstract class AppAction extends ReduxAction<AppState> {
ActionSelect get select => ActionSelect(state);
}
// Dedicated selector class to keep the base action clean.
class ActionSelect {
final AppState state;
ActionSelect(this.state);
}
Add the following information to the project's CLAUDE.md, so that all actions extend
this
base action:
## Base Action
All actions should extend `AppAction` instead of `ReduxAction<AppState>`.
There is a dedicated selector class called `ActionSelect` to keep the base action clean,
by namespacing selectors under `select` and enabling IDE autocompletion. Example:
```dart
class ProcessItem extends AppAction {
final String itemId;
ProcessItem(this.itemId);
@override
AppState reduce() {
// IDE autocomplete shows: select.findById, select.completed, etc.
final item = select.findById(itemId);
// ...
}
}