NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
pub.dev · #3162 most downloaded on pub.dev
The modern version of Redux. State management that's simple to learn and easy to use; Powerful enough to handle complex applications with millions of users; Testable.
Last release 2 days ago
06 Oct 2026
Release timing varies
gaps range from 9 days to 2 months
Some releases are documented
notes for 30 of the last 60 stable releases
1 version withdrawn
withdrawn after publishing
7 years old
277 releases · first in 2019
Flutter 3.7.5, Dart 2.19.2, fast_immutable_collections: 9.0.0.
Flutter 3.7.5, Dart 2.19.2, fast_immutable_collections: 9.0.0.
BREAKING: The Action.wrapError(error, stackTrace) method now also
gets the
stacktrace
instead of just the error. If your code breaks, just add the extra parameter,
like so:
Object wrapError(error) => ... turns into
Object wrapError(error, _) => ...
<br>
Persistor is provided to the Store, it now considers
the
initialState is already persisted. Before this change, it considered nothing
was
persisted. Note: Before you create the store, you are allowed to call the
Persistor
methods
directly: Persistor.saveInitialState(), readState() and deleteState().
However, after you create the store, please don't call those methods yourself
anymore.
If you do it, AsyncRedux cannot keep track of which state was persisted. After
store
creation,
if necessary, you should use the corresponding methods
Store.saveInitialStateInPersistence(),
Store.readStateFromPersistence() and Store.deleteStateFromPersistence().
These
methods let
AsyncRedux keep track of the persisted state, so that it's able to call
Persistor.persistDifference() with the appropriate parameters.<br>
Store.getLastPersistedStateFromPersistor() returns the state that was
last persisted to the local persistence. It's unlikely you will use this
method yourself.<br>
BREAKING: The factory declaration used to have two type parameters, but
now it
has three:
class Factory extends VmFactory<AppState, MyConnector, MyViewModel>
With that change, you can now reference the view-model inside the Factory
methods, by
using
the vm getter. Example:
ViewModel fromStore() =>
ViewModel(
value: _calculateValue(),
onTap: _onTap);
}
void _onTap() => dispatch(SaveValueAction(vm.value)); // Use the value from the vm.
Note 1: You can only use the vm getter after the fromStore() method is
called, which
means
you cannot reference the vm inside of the fromStore() method itself. If
you do that,
you'll get a StoreException. You also cannot use the vm getter if the
view-model is
null.
Note 2: To reduce boilerplate, and not having to pass the AppState type
parameter
whenever you
create a Factory, I recommend you define a base Factory, like so:
abstract class BaseFactory<T extends Widget?, Model extends Vm> extends VmFactory<AppState, T, Model> {
BaseFactory([T? connector]) : super(connector);
}
Added class LocalJsonPersist to help persist the state as pure Json.
One column per quarter.
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Fixed small bug when persistor is paused before being used once.
Nothing published for this version
* Version bump of dependencies.
Fixed issue with the StoreConnector.shouldUpdateModel method when the widget updates.
The StateObserver.observe() method signature changed to include an error parameter: ` void observe( ReduxAction action, St stateIni, St stateEnd, Obje
The StateObserver.observe() method signature changed to include an error
parameter:
void observe(
ReduxAction<St> action,
St stateIni,
St stateEnd,
Object? error,
int dispatchCount,
);
The state-observers are now also called when the action reducer complete with
a error.
In this case, the error object will not be null. This makes it easier to use
state-observers
for metrics. Please, see the documentation for the recommended clean-code way
to do
this.
Added another cache function, for 2 states and 3 parameters: cache2states_3params.
cache2states_3params.The above breaking change is unlikely to affect you in any way, but if you want the old behavior, just add await microtask; to the first line of your…
BREAKING: Async reduce() methods (those that return Futures) are now
called
synchronously (in the same microtask of their dispatch), just like a regular
async
function is.
In other words, now dispatching a sync action works just the same as calling a
sync
function,
and dispatching an async action works just the same as calling an async
function.
// Example: The below code will print: "BEFORE a1 f1 AFTER a2 f2"
print('BEFORE');
dispatch(MyAsyncAction());
asyncFunction();
print('AFTER');
class MyAsyncAction extends ReduxAction<AppState> {
Future<AppState?> reduce() async {
print('a1');
await microtask;
print('a2');
return state;
}
}
Future<void> asyncFunction() async {
print('f1');
await Future.microtask((){});
print('f2');
}
Before version 16.0.0, the reduce() method was called in a later
microtask. Please
note, the
async reduce() methods continue to return and apply the state in a later
microtask (this did
not change).
The above breaking change is unlikely to affect you in any way, but if you
want the old behavior, just add await microtask; to the first line of your
reduce() method.
<br>
BREAKING: When your reducer is async (i.e., returns Future<AppState>)
you must
make sure
you do not return a completed future, meaning all execution paths of the
reducer
must pass
through at least one await keyword. In other words, don't return a Future if
you don't
need it.
If your reducer has no awaits, you must return AppState? instead of
Future<AppState?>, or
add await microtask; to the start of your reducer, or return null. For
example:
// These are right:
AppState? reduce() { return state; }
AppState? reduce() { someFunc(); return state; }
Future<AppState?> reduce() async { await someFuture(); return state; }
Future<AppState?> reduce() async { await microtask; return state; }
Future<AppState?> reduce() async { if (state.someBool) return await calculation(); return null; }
// But these are wrong:
Future<AppState?> reduce() async { return state; }
Future<AppState?> reduce() async { someFunc(); return state; }
Future<AppState?> reduce() async { if (state.someBool) return await calculation(); return state; }
If you don't follow this rule, AsyncRedux may seem to work ok, but will eventually misbehave.
It's generally easy to make sure you are not returning a completed future.
In the rare case your reducer function is very complex, and you are unsure
that all code paths pass through an await, just
add assertUncompletedFuture(); at the very END of your reduce
method, right before the return. If you do that, an error will be shown in
the console if the reduce method ever returns a completed future.
If you're an advanced user interested in the details, check the <a href="https://github.com/marcglasberg/async_redux/blob/master/test/sync_async_test.dart"> sync/async tests</a>.
<br>
When the Event class was created, Flutter did not have another class with
that name.
Now there is. For this reason, a typedef now allows you to use Evt instead.
If you need, you can hide one of them, by importing AsyncRedux like this:
import 'package:async_redux/async_redux.dart' hide Event;
or
import 'package:async_redux/async_redux.dart' hide Evt;
* Flutter 3.0 support.
Nothing published for this version
NavigateAction.popUntilRouteName() can print the routes (for debugging).
NavigateAction.popUntilRouteName() can print the routes (for debugging).Nothing published for this version
Better stacktrace for wrapped errors in actions.
The store persistor can now be paused and resumed, with methods store.pausePersistor(), store.persistAndPausePersistor() and store.resumePersistor().
The store persistor can now be paused and resumed, with methods
store.pausePersistor(),
store.persistAndPausePersistor() and store.resumePersistor(). This may be
used
together with
the app lifecycle, to prevent a persistence process to start when the app is
being shut
down. For
example:
child: StoreProvider<AppState>(
store: store,
child: AppLifecycleManager( // Add this widget here to capture lifecycle events.
child: MaterialApp(
...
class AppLifecycleManager extends StatefulWidget {
final Widget child;
const AppLifecycleManager({Key? key, required this.child}) : super(key: key);
_AppLifecycleManagerState createState() => _AppLifecycleManagerState();
}
class _AppLifecycleManagerState extends State<AppLifecycleManager> with WidgetsBindingObserver {
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
void didChangeAppLifecycleState(AppLifecycleState lifecycle) {
store.dispatch(ProcessLifecycleChange_Action(lifecycle));
}
Widget build(BuildContext context) => widget.child;
}
class ProcessLifecycleChangeAction extends ReduxAction<AppState> {
final AppLifecycleState lifecycle;
ProcessLifecycleChangeAction(this.lifecycle);
Future<AppState?> reduce() async {
if (lifecycle == AppLifecycleState.resumed || lifecycle == AppLifecycleState.inactive) {
store.resumePersistor();
} else if (lifecycle == AppLifecycleState.paused || lifecycle == AppLifecycleState.detached) {
store.persistAndPausePersistor();
} else
throw AssertionError(lifecycle);
return null;
}
}
When logging out of the app, you can call store.deletePersistedState() to
ask the
persistor to
delete the state from disk.
BREAKING: This is a very minor change, unlikely to affect you. The
signature for
the Action.wrapError method has changed from Object? wrapError(error)
to Object? wrapError(Object error). If you get an error when you upgrade,
you can fix
it by
changing the method that broke into Object? wrapError(dynamic error).
BREAKING: Context is now nullable for these StoreConnector methods:
void onInitialBuildCallback(BuildContext? context, Store<St> store, Model viewModel);
void onDidChangeCallback(BuildContext? context, Store<St> store, Model viewModel);
void onWillChangeCallback(BuildContext? context, Store<St> store, Model previousVm, Model newVm);
Nothing published for this version
* Version bump of dependencies.
Nothing published for this version
* Version bump of dependencies.
delay parameter for WaitAction.add() and WaitAction.remove() methods.
delay parameter for WaitAction.add() and WaitAction.remove() methods.Added missing dispatchSync and dispatchAsync to StoreTester. Note: dispatchAsync was later renamed to dispatchAndWait.
dispatchSync and dispatchAsync to StoreTester.
Note: dispatchAsync was later renamed to dispatchAndWait.Added missing dispatchSync to VmFactory.
dispatchSync to VmFactory.dispatch can be used to dispatch both sync and async actions. It returns a FutureOr. You can await the result or not, as desired.
dispatch can be used to dispatch both sync and async actions. It returns a
FutureOr.
You can
await the result or not, as desired.
dispatchAsync can also be used to dispatch both sync and async actions. But
it always
returns a
Future (not a FutureOr). Use this only when you explicitly need a
Future, for
example, when
working with the RefreshIndicator widget. Note: dispatchAsync was later
renamed
to dispatchAndWait.
dispatchSync allows you to dispatch SYNC actions only. In that case,
dispatchSync(action) is
exactly the same as dispatch(action). However, if your action is ASYNC,
dispatchSync
will
throw an error. Use this only when you need to make sure an action is sync (meaning it
impacts the
store state immediately when it returns). This is not very common. Important:
An action
is sync if
and only if both its before and reduce methods are sync. If any or both
these
methods return a
Future, then the action is async and will throw an error when used with
dispatchSync.
StoreTester.getConnectorTester helps test StoreConnectors methods, such as
onInit,
onDispose and onWillChange. For example, suppose you have a
StoreConnector which
dispatches SomeAction on its onInit. You could test it like this:
class MyConnector extends StatelessWidget {
Widget build(BuildContext context) => StoreConnector<AppState, Vm>(
vm: () => _Factory(),
onInit: _onInit,
builder: (context, vm) { ... }
}
void _onInit(Store<AppState> store) => store.dispatch(SomeAction());
}
var storeTester = StoreTester(...);
var connectorTester = storeTester.getConnectorTester(MyConnector());
connectorTester.runOnInit();
var info = await tester.waitUntil(SomeAction);
For more information, see section Testing the StoreConnector in the README.md file.
Fix: UserExceptionDialog now shows all UserExceptions. It was discarding
some of
them under
some circumstances, in a regression created in version 4.0.4.
In the Store constructor you can now set maxErrorsQueued to control the
maximum
number of
errors the UserExceptionDialog error-queue can hold. Default is 10.
ConsoleActionObserver is now provided to print action details to the
console.
WaitAction.toString() now returns a better description.
Nothing published for this version
Nothing published for this version
Nothing published for this version
BREAKING: Improved state typing for some Store parameters. You will now have to use Persistor instead of Persistor, and WrapError instead of WrapError
BREAKING: Improved state typing for some Store parameters. You will
now have to use Persistor<AppState> instead of Persistor, and WrapError<AppState>
instead of WrapError etc.
Global Store(wrapReduce: ...). You may now globally wrap the reducer to
allow for some
pre or
post-processing. Note: if the action also have a wrapReduce method, this
global wrapper
will be
called AFTER (it will wrap the action's wrapper which wraps the action's
reducer).
Downgraded dev_dependencies test: ^1.16.0
You can now provide callbacks onOk and onCancel to an UserException. This allows you to dispatch actions when the user dismisses the error dialog. Whe
onOk and onCancel to an UserException.
This allows
you to
dispatch actions when the user dismisses the error dialog. When using the
default UserExceptionDialog: (i) if only onOk is provided, it will be
called when
the dialog
is dismissed, no matter how. (ii) If both onOk and onCancel are provided,
then
onOk will be
called only when the OK button is pressed, while onCancel will be called
when the
dialog is
dismissed by any other means.BREAKING: ReduxAction.hasFinished() has been deprecated. It should be renamed to isFinished.
BREAKING: The dispatchFuture function is not necessary anymore. Just
rename it
to dispatch, since now the dispatch function always returns a future, and
you can
await it or
not, as desired.
BREAKING: ReduxAction.hasFinished() has been deprecated. It should be
renamed to isFinished.
The dispatch function now returns an ActionStatus. Usually you will
discard this
info, but you
may use it to know if the action completed with no errors. For example,
suppose a
SaveAction
looks like this:
class SaveAction extends ReduxAction<AppState> {
Future<AppState> reduce() async {
bool isSaved = await saveMyInfo();
if (!isSaved) throw UserException("Save failed.");
...
}
}
Then, when you save some info, you want to leave the current screen if and only if the save process succeeded:
var status = await dispatch(SaveAction(info));
if (status.isFinished) dispatch(NavigateAction.pop()); // Or: Navigator.pop(context)
Even if you use this dialog, it is unlikely this will be a breaking change for you. But if it is, and your error dialog now has problems, simply make…
BREAKING: The new UserExceptionDialog.useLocalContext parameter now
allows the UserExceptionDialog to be put in the builder parameter of the
MaterialApp widget. Even if you use this dialog, it is unlikely this will be a
breaking change for you. But if it is, and your error dialog now has problems, simply
make useLocalContext: true to return to the old behavior.
BREAKING: StoreConnector parameters onInitialBuild, onDidChange
and onWillChange now also get the context and the store. For example, where
you previously
had onInitialBuild(vm) {...} now you have
onInitialBuild(context, store, vm) {...}.
Nothing published for this version
LocalPersist saveJson() and loadJson() methods.
saveJson() and loadJson() methods.* FIC and weak-map version bump.
* NNBD improvements. * FIC version bump.
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Downgrade to file: ^6.0.0 to improve compatibility.
* Nullsafe.
Nothing published for this version
Uses nullsafe dependencies (it's not yet itself nullsafe).
Uses nullsafe dependencies (it's not yet itself nullsafe).
BREAKING: Cache functions (for memoization) have been renamed and extended.
LocalPersist: Better handling of mock file-systems.
` // Deprecated. vm: MyFactory(this), `
BREAKING:
Now the vm parameter in the StoreConnector is a function that creates a
VmFactory (instead
of being a VmFactory object itself).
So, to upgrade, you just need to provide this:
vm: () => MyFactory(this),
Instead of this:
// Deprecated.
vm: MyFactory(this),
Now the StoreConnector will create a VmFactory every time it needs a
view-model. The
Factory
will have access to:
state getter: The state the store was holding when the factory and the
view-model
were
created. This state is final inside the factory.
currentState() method: The current (most recent) store state. This will
return
the current
state the store holds at the time the method is called.
New store parameter immutableCollectionEquality lets you override the
equality used
for
immutable collections from
the <a href="https://pub.dev/packages/fast_immutable_collections">
fast_immutable_collections</a> package.
Nothing published for this version
* StoreTester.dispatchState ().
VmFactory.getAndRemoveFirstError ().
NavigateAction now closely follows the Navigator api: push(), pop(), popAndPushNamed(), pushNamed(), pushReplacement(), pushAndRemoveUntil(), replace(
NavigateAction now closely follows the Navigator api: push(),
pop(), popAndPushNamed(), pushNamed(), pushReplacement(),
pushAndRemoveUntil(),
replace(), replaceRouteBelow(), pushReplacementNamed(),
pushNamedAndRemoveUntil(),
pushNamedAndRemoveAll(), popUntil(), removeRoute(),
removeRouteBelow(),
popUntilRouteName() and popUntilRoute().Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Your coding agent can read these notes before it upgrades. Set up the MCP server →