Advanced waiting
It is common to show a visual hint that an important process is running and should finish soon. Examples include:
-
A save button showing a spinner while saving.
-
A
Text("Please wait...")on screen while something is being calculated. -
A shimmer used as a placeholder while data loads.
-
A modal barrier that blocks user interaction while a screen opens.
In Wait, fail, succeed I explain that the easiest way to handle this is to use:
isWaiting(MyAction)in actionscontext.isWaiting(MyAction)in widgets
Here, MyAction is the async action that runs the process you are waiting for.
This approach is very easy to use and fits most situations.
If you find a case where isWaiting() is not enough, keep reading.
WaitAction and Wait
In Before and After the Reducer section I show
how to manually create a BarrierAction with a boolean flag used to add or remove a modal
barrier in the screen (see the
code here).
However, keeping track of many such boolean flags may be difficult to do.
An option is using the built-in classes WaitAction and Wait.
To use them, your store state must have a Wait field named wait, and then your state class
must have a copy or copyWith method which copies this field as a named parameter. For example:
class AppState {
final Wait wait;
...
AppState({this.wait, ...});
AppState copy({Wait wait, ...}) => AppState(wait: wait ?? this.wait, ...);
}
Then, when you want to start waiting,
dispatch a WaitAction and pass it some immutable object to act as a flag.
When you finish waiting, remove the flag.
For example:
dispatch(WaitAction.add("my flag")); // To add a flag.
dispatch(WaitAction.remove("my flag")); // To remove a flag.
You can then use this "wait state" in your actions and widgets:
state.wait.isWaitingAnyreturnstrueif there's any waiting whatsoever.state.wait.isWaiting(flag)returnstrueif you are waiting for a specificflagstate.wait.isWaiting(flag, ref: reference)returnstrueif you are waiting for a specificreferenceof theflag.state.wait.isWaitingForType<T>()returnstrueif you are waiting for any flag of typeT.
Flag types
The flag can be any convenient immutable object, like a URL, a user id, an index, an enum, a String, a number, or other.
As an example, if we want to replace the isWaiting() method with the Wait object, we
could do the following:
class LoadAction extends AppAction {
Future<AppState> reduce() async {
var newText = await loadText();
return state.copy(text: newText);
}
void before() => dispatch(WaitAction.add(this)); // Here!
void after() => dispatch(WaitAction.remove(this)); // Here!
}
Then, in the widget:
if (wait.isWaitingForType<LoadAction>()) { // Show the button as disabled }
else { // Show the button as enabled }
Note: You may also define a mixin to implement the waiting:
mixin WithWaitState implements ReduxAction<AppState> {
void before() => dispatch(WaitAction.add(this));
void after() => dispatch(WaitAction.remove(this));
}
class LoadAction extends AppAction with WithWaitState {
Future<AppState> reduce() async {
var newText = await loadText();
return state.copy(text: newText);
}
}
Try running
the: Wait Action Simple Example
(which is similar to the
Before and After example
but using the built-in WaitAction). It uses the action itself as the
flag, by passing this.
Granular flags
A more advanced example is
the Wait Action Advanced 1 Example.
Here, 10 buttons are shown. When a button is clicked it will be
replaced by a downloaded text description. Each button shows a progress indicator while its
description is downloading. Also, the screen title shows the text "Downloading..." if any of the
buttons is currently downloading.

The flag in this case is simply the index of the button, from 0 to 9:
int index;
void before() => dispatch(WaitAction.add(index));
void after() => dispatch(WaitAction.remove(index));
If there's any waiting, then state.wait.isWaitingAny will return true.
However, now you can check each button wait flag separately by its index,
as state.wait.isWaiting(index) will return true only when that specific button is waiting.
If necessary, you can also clear all flags by doing dispatch(WaitAction.clear()).
Wait references
If you fear your flag may conflict with others, you can also add a "namespace", by further dividing flags into references. This can be seen in the Wait Action Advanced 2 Example:
void before() => dispatch(WaitAction.add("button-download", ref: index));
void after() => dispatch(WaitAction.remove("button-download", ref: index));
Now, to check a button's wait flag, you must pass both the flag and the reference:
state.wait.isWaiting("button-download", ref: index).
If necessary, you can clear all references of that flag with dispatch(WaitAction.clear("button-download")).
You can also pass a delay value to WaitAction.add() and WaitAction.remove() methods. Please refer to
their method documentation for more information.
Third-party package support
How can I use Freezed, BuiltValue, or other similar code generator packages with the WaitAction?
In case you use
built_value
or freezed packages, the WaitAction works out-of-the-box with them.
In both cases, you don't need to create the state's copy or copyWith methods by hand.
But you still need to add the Wait object to the store state as previously described.
If you want to further customize WaitAction to work with other packages, or to use the Wait
object in different ways, you can do so by injecting your custom reducer into WaitAction.reducer
during your app's initialization. Refer to
the WaitAction
documentation for more information.